2019-03-25 14:12:30 The_dream_is_a_verb 阅读数 339
  • 微信小程序系统教程Java版[3/3阶段]_微信小程序支付

    微信小程序系统教程[初级阶段],微信小程序0基础学起,讲解微信小程序开发的基础知识。 微信小程序系统教程共有“微信小程序系统教程[初级阶段]”、“微信小程序系统教程[中级阶段]——核心技术”、“微信小程序系统教程[阶段]客服消息+微信支付+九宝电商系统”。 “微信小程序系统教程[阶段]全套课程”包含: 1.微信小程序系统教程[阶段]_客服消息 2.微信小程序系统教程[阶段]_微信支付 3.微信小程序系统教程[阶段]_九宝电商系统 学习“微信小程序系统教程[阶段]”要求有微信小程序的基础。建议先学习“微信小程序系统教程[初级阶段]”、“微信小程序系统教程[中级阶段]”,后在报名“微信小程序系统教程[阶段]”。 阶段讲解的客服消息,是针对小程序的。后台程序用接近底层的技术,没有使用三方技术。这样降低同学们学习成本。 微信支付,这部分课程很有难度,要求同学们认真听讲,有不会的技术问题可以请教老师。购买课程后请联系老师,提供单号,给你源程序。 九宝电商系统是一套十分适和学习、项目的课程。既可以系统的学习微信小程序相关知识,还可以修改后上线。 “微信小程序系统教程[中级阶段]——核心技术”重点讲解微信小程序事件、组件、API 微信小程序系统教程[初级阶段],微信小程序0基础学起,讲解微信小程序开发的基础知识。 购买课程的同学,可赠送就九宝老师编写的《微信小程序开发宝典》。 购课请咨询qq2326321088

    4263 人正在学习 去看看 翟东平

微信小程序支付前需要统一下单,下单成功,返回json对象。获取json中值拿去支付

微信小程序我是用nui框架,发送一个下单请求:

                     //下单请求
                    uni.request({
                    url: "....",//自己后端链接
					data: {
						//传一些值,比如金额,商品信息,用户id 啥的
					}, 
					success: (res) => {
						uni.hideLoading();
						let resdata = res.data;
                        //下单失败
						if (resdata.rn_code == "FAIL") {
							uni.showToast({
								title: "订单异常"
							})
							return;
						}
                        //下单
						if (resdata.rn_code == "SUCCESS") {
							uni.showToast({
								title: "提交成功"
							});
							let timeSp = resdata.timeStamp;
							let nonceSr = resdata.nonceStr;
							let pacKe = resdata.package;
							let paySn = resdata.paySign;
							this.payFun(timeSp, nonceSr, pacKe,paySn)
						}
					},
					fail: (e) => {
						uni.showToast({
							title: "网络异常!"
						});
					}
				})


            //支付函数
			payFun: function(tSp, neStr, pae, paySign) {
				// console.log(paySign)
				uni.requestPayment({
					provider: 'wxpay',
					timeStamp: tSp,
					nonceStr: neStr,
					package: pae,
					signType: 'MD5',
					paySign: paySign,
					success: function(res) {
						 console.log('success:' + JSON.stringify(res));
						let rsMsg = res.errMsg;
						if (rsMsg == "requestPayment:ok") {
							// this.paySuccess();
							uni.showToast({
								title: "支付成功",
								duration: 2000
							});
						}

						uni.navigateBack();
						uni.navigateTo({
							url: "../user/my_order?current=0"
						})
					},
					fail: function(err) {
						// console.log('fail:' + JSON.stringify(err));
						let rsMsg = err.errMsg;
						// if(rsMsg=="requestPayment:fail cancel"){
						uni.showToast({
							title: "未成功支付!",
							duration: 2000
						});
					
					   
					}
				});
			},




			

java两个函数1.统一下单,2.支付成功回调函数:

/**
     * 微信小程序-订单-生成
     */
    @RequestMapping(value = "/WX_order_generate")
    public String WX_order_generate(String openId, HttpServletRequest request)throws Exception {
        JSONObject jObject;
        String payMy="";
        //用户id  openId
        //获取真实IP
        String spbill_IP=IpUtil.getIpAddr(request);
        //获取随机订单号
        String or_id=WxConfig.getRandomString(32);
        //统一下单。。返回JsonObj
        jObject = WxConfig.WxXiaDan(or_id,openId,"商品标题",payMy,spbill_IP);
        String rnCode= (String) jObject.get("rn_code");//获取返回码
        if(rnCode.equals("FAIL")){//失败
            return jObject.toString();
        }
        if(rnCode.equals("SUCCESS")) {//成功
            //返回json中数据、传过来的值,保存在自己表里
            //int totalInt= userServices.WX_add_order();
            //if (totalInt==0){
              //  jObject.put("rn_code", "FAIL");
            //}
        }
        return jObject.toString();
    }




 /**
     * 微信小程序支付成功回调函数
     * @param request
     * @param response
     * @throws Exception
     */
    @RequestMapping(value = "/WX/callback")
    public void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(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 = "";
        System.out.println("接收到的报文:" + notityXml);
        Map map = PayUtil.doXMLParse(notityXml);
        String returnCode = (String) map.get("return_code");
        if("SUCCESS".equals(returnCode)){
            //验证签名是否正确
            Map<String, String> validParams = PayUtil.paraFilter(map);  //回调验签时需要去除sign和空值参数
            String validStr = PayUtil.createLinkString(validParams);//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串
            String sign = PayUtil.sign(validStr, WxConfig.Mch_key, "utf-8").toUpperCase();//拼装生成服务器端验证的签名
            // 因为微信回调会有八次之多,所以当第一次回调成功了,那么我们就不再执行逻辑了

            //根据微信官网的介绍,此处不仅对回调的参数进行验签,还需要对返回的金额与系统订单的金额进行比对等
            if(sign.equals(map.get("sign"))){

                /**此处添加自己的业务逻辑代码start**/
//                System.out.println("订单支付的金额:" +  map.get("cash_fee"));
//                System.out.println("订单支付的用户openid:" +  map.get("openid"));
//                System.out.println("订单支付状态:" +  map.get("result_code"));
//                System.out.println("订单编号:" +  map.get("nonce_str"));

                String orderId = (String) map.get("nonce_str");
                String wxOpenId = (String) map.get("openid");

//              需要支付的单位是分 , total_fee需要支付的金额  cash_fee实际支付金额,扣除红包后金额
                String total_fee = (String) map.get("total_fee");
                int cashFee = Integer.valueOf(total_fee);
                String checkFee = String.valueOf(cashFee / 100);
//                System.out.println("订单验证的金额:" +  checkFee);

                /**可以保存修改支付状态信息**/
                /**此处添加自己的业务逻辑代码end**/
                //通知微信服务器已经支付成功
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
            } else {
                System.out.println("微信支付回调失败!签名不一致");
            }
        }else{
            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();
    }

 

微信小程序支付的处理类(核心):

package com.WxPay;

import com.alibaba.fastjson.JSONObject;
import com.toolUtils.DateUtils;
import com.toolUtils.MD5Util;
import com.toolUtils.PayUtil;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.ParseException;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

public class WxConfig {
    private static String AppID = "";// 小程序ID
    private static String Secret = "";// 小程序 Secret
    private static String Mch_id = "";// 商户号
    public static String Mch_key = "";// 商户号 key
    //支付成功后的自己服务器回调url
    private static String notify_url = "https://??????.com/WX/callback";
    //签名方式,固定值
    private static String SIGNTYPE = "MD5";
    //交易类型,小程序支付的固定值为JSAPI
    private static String TRADETYPE = "JSAPI";
    //微信统一下单接口地址
    private static String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";



    public static JSONObject WxXiaDan(String or_id,String openId,String gsTitle,String totalFee ,String spbill_IP ) throws Exception {

        String body =gsTitle;// 商品描述
//        String notify_url = ""; // 支付成功的回调地址  可访问 不带参数
        String nonce_str = or_id;//随机字符串
        String out_trade_no = or_id;// 商户订单号
//        String total_fee = "1";//总金额 单位为分
        String total_fee = totalFee;//总金额 单位为分
//        System.out.println("支付的金额:"+total_fee);
//        int timestamp = Math.round(new Date().getTime() / 1000); // 当前时间

        //获取客户端的ip地址
        String spbill_create_ip = spbill_IP;
//        IpUtil.getIpAddr(request);
        //组装参数,用户生成统一下单接口的签名
        Map<String, String> packageParams = new HashMap<>();
        packageParams.put("appid", AppID);
        packageParams.put("mch_id", Mch_id);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("body", body);
        packageParams.put("out_trade_no", out_trade_no);//商户订单号
        packageParams.put("total_fee", total_fee);//支付金额,这边需要转成字符串类型,否则后面的签名会失败
        packageParams.put("spbill_create_ip", spbill_create_ip);
        packageParams.put("notify_url", notify_url);//支付成功后的回调地址
        packageParams.put("trade_type", TRADETYPE);//支付方式
        packageParams.put("openid", openId);

        String preStr = PayUtil.createLinkString(packageParams); // 把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串

        //MD5运算生成签名,这里是第一次签名,用于调用统一下单接口
        String mysign = PayUtil.sign(preStr, Mch_key, "utf-8").toUpperCase();
        //拼接统一下单接口使用的xml数据,要将上一步生成的签名一起拼接进去
        String xml = "<xml>" + "<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>" + out_trade_no + "</out_trade_no>"
                + "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>"
                + "<total_fee>" +total_fee + "</total_fee>"
                + "<trade_type>" + TRADETYPE + "</trade_type>"
                + "<sign>" + mysign + "</sign>"
                + "</xml>";
        System.out.println("调试模式_统一下单接口 请求XML数据:" + xml);
        //调用统一下单接口,并接受返回的结果
        String result = PayUtil.httpRequest(pay_url, "POST", xml);
        System.out.println("调试模式_统一下单接口 返回XML数据:" + result);
        // 将解析结果存储在HashMap中
        Map map = PayUtil.doXMLParse(result);
        String return_code = (String) map.get("return_code");//返回状态码

        JSONObject jObject = new JSONObject();//返回给小程序端需要的参数
        jObject.put("rn_code", return_code);
        if(return_code.equals("SUCCESS")){
            String prepay_id = (String) map.get("prepay_id");//返回的预付单信息
            jObject.put("nonceStr", nonce_str);
            jObject.put("package", "prepay_id=" + prepay_id);
            Long timeStamp = System.currentTimeMillis() / 1000;
            jObject.put("timeStamp", timeStamp + "");//这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
            //拼接签名需要的参数
            String stringSignTemp = "appId=" + AppID + "&nonceStr=" + nonce_str + "&package=prepay_id=" + prepay_id+ "&signType=MD5&timeStamp=" + timeStamp;
            //再次签名,这个签名用于小程序端调用wx.requesetPayment方法
            String paySign = PayUtil.sign(stringSignTemp, Mch_key, "utf-8").toUpperCase();
            jObject.put("paySign", paySign);
        }
        jObject.put("appid", AppID);
        return jObject;
    }


  public  static  String  loginRequest(String getCode){
        String urlSr="https://api.weixin.qq.com/sns/jscode2session?appid="+AppID+"&secret="+Secret+"&js_code="+getCode+"&grant_type=authorization_code";

      String result = PayUtil.httpRequest(urlSr, "POST", null);
      System.out.println(result);

      return result;
  }


    public static String getAccess_token() {
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + AppID + "&secret=" + Secret;
        String accessToken = null;
        try {
            URL urlGet = new URL(url);
            HttpURLConnection http = (HttpURLConnection) urlGet
                    .openConnection();
            http.setRequestMethod("GET"); // 必须是get方式请求
            http.setRequestProperty("Content-Type",
                    "application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);
            System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
            System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
            http.connect();
            InputStream is = http.getInputStream();
            int size = is.available();
            byte[] jsonBytes = new byte[size];
            is.read(jsonBytes);
            accessToken = new String(jsonBytes, "UTF-8");
            System.out.println("获取accessToken值:" + accessToken);
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        JSONObject result = JSONObject.parseObject(accessToken);
        Map<String, String> map = JSONObject.toJavaObject(result, Map.class);
        System.out.println(map.get("access_token"));
        return map.get("access_token");

    }




    /*
     *  (随机数+当前时间)再MD5加密,长度在32位以内
     * */
    public static String getRandomMD5() throws ParseException {
        String random = DateUtils.getRandom() + DateUtils.getNewTimeyMd();
        String md5DateName = MD5Util.EncryptionMD5(random);
        return md5DateName;
    }



    /*
     *  获取随机字符串,传入字符串长度
     * */
    public static String getRandomString(int length) {
        //产生随机数
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        //循环length次
        for (int i = 0; i < length; i++) {
            //产生0-2个随机数,既与a-z,A-Z,0-9三种可能
            int number = random.nextInt(3);
            long result = 0;
            switch (number) {
                //如果number产生的是数字0;
                case 0:
                    //产生A-Z的ASCII码
                    result = Math.round(Math.random() * 25 + 65);
                    //将ASCII码转换成字符
                    sb.append(String.valueOf((char) result));
                    break;
                case 1:
                    //产生a-z的ASCII码
                    result = Math.round(Math.random() * 25 + 97);
                    sb.append(String.valueOf((char) result));
                    break;
                case 2:
                    //产生0-9的数字
                    sb.append(String.valueOf(new Random().nextInt(10)));
                    break;
            }
        }
        return sb.toString();
    }

    //商户订单号
    public static String getWxPayOrderID() {
        Calendar calendar = Calendar.getInstance();
        //年份
        int year = calendar.get(Calendar.YEAR);
        //月份
        int mouth = calendar.get(Calendar.MONTH) + 1;
        //日期
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        //小时
        int hour = calendar.get(Calendar.HOUR);
        //分
        int minute = calendar.get(Calendar.MINUTE);
        //秒
        int second = calendar.get(Calendar.SECOND);
        //毫秒
        int mSecond = calendar.get(Calendar.MILLISECOND);

        String mouthStr = String.valueOf(mouth);
        String dayStr = String.valueOf(day);
        String hourStr = String.valueOf(hour);
        String minuteStr = String.valueOf(minute);
        String secondStr = String.valueOf(second);
        String mSecondStr = String.valueOf(mSecond);
        if (mouth < 10) {
            mouthStr = 0 + String.valueOf(mouth);
        }
        if (day < 10) {
            dayStr = 0 + String.valueOf(mouth);
        }
        if (hour < 10) {
            hourStr = 0 + String.valueOf(hour);
        }
        if (minute < 10) {
            minuteStr = 0 + String.valueOf(minute);
        }
        if (second < 10) {
            secondStr =0 + String.valueOf(second);
        }
        if (mSecond < 10) {
            mSecondStr = 00 + String.valueOf(mSecond);
        } else if (mSecond >= 10 && mSecond < 100) {
            mSecondStr =0 + String.valueOf(second);
        }
        String currentDate = year + mouthStr + dayStr + hourStr + minuteStr + secondStr + mSecondStr;
        return currentDate;
    }
    
    /**
     * IpUtils工具类方法
     * 获取真实的ip地址
     *
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            //多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
        ip = request.getHeader("X-Real-IP");
        if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
            return ip;
        }
        return request.getRemoteAddr();


    }

}

 

2019-05-27 20:01:01 u011134780 阅读数 3708
  • 微信小程序系统教程Java版[3/3阶段]_微信小程序支付

    微信小程序系统教程[初级阶段],微信小程序0基础学起,讲解微信小程序开发的基础知识。 微信小程序系统教程共有“微信小程序系统教程[初级阶段]”、“微信小程序系统教程[中级阶段]——核心技术”、“微信小程序系统教程[阶段]客服消息+微信支付+九宝电商系统”。 “微信小程序系统教程[阶段]全套课程”包含: 1.微信小程序系统教程[阶段]_客服消息 2.微信小程序系统教程[阶段]_微信支付 3.微信小程序系统教程[阶段]_九宝电商系统 学习“微信小程序系统教程[阶段]”要求有微信小程序的基础。建议先学习“微信小程序系统教程[初级阶段]”、“微信小程序系统教程[中级阶段]”,后在报名“微信小程序系统教程[阶段]”。 阶段讲解的客服消息,是针对小程序的。后台程序用接近底层的技术,没有使用三方技术。这样降低同学们学习成本。 微信支付,这部分课程很有难度,要求同学们认真听讲,有不会的技术问题可以请教老师。购买课程后请联系老师,提供单号,给你源程序。 九宝电商系统是一套十分适和学习、项目的课程。既可以系统的学习微信小程序相关知识,还可以修改后上线。 “微信小程序系统教程[中级阶段]——核心技术”重点讲解微信小程序事件、组件、API 微信小程序系统教程[初级阶段],微信小程序0基础学起,讲解微信小程序开发的基础知识。 购买课程的同学,可赠送就九宝老师编写的《微信小程序开发宝典》。 购课请咨询qq2326321088

    4263 人正在学习 去看看 翟东平

在开发微信小程序支付的功能前,我们先熟悉下微信小程序支付的业务流程图:

å°ç¨åºæ¯ä»æ¶åºå¾

不熟悉流程的建议还是仔细阅读微信官方的开发者文档

一,准备工作

事先需要申请企业版小程序,并开通“微信支付”(即商户功能)。并获取一下参数:

appid=********  //小程序appid
mchid=********  //小程序绑定商户id
key=*****************  //商户后台设置的key

并在商户后天设置开发者选项,主要是设置回调域名。

二,Java后台代码编写

Controller层代码:

@RestController
@RequestMapping(value = "/payment/")
public class PaymentController {

	private static Logger logger = LoggerFactory.getLogger(PaymentController.class);
	@Value("${hcc.wx.domain}")
	private String orderDomain;
	
	@Autowired
	private PaymentService paymentService;

     /**
	 * <p>统一下单入口</p>
	 *
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	@ResponseBody
    @RequestMapping(value="toPay", method=RequestMethod.POST, 
produces ={"application/json;charset=UTF-8"})
	public JSONObject toPay(HttpServletRequest request) throws Exception {
		String requestStr = RequestStr.getRequestStr(request);
		if (StringUtils.isEmpty(requestStr)) {
			throw new ParamException();
		}
		JSONObject jsonObj = JSONObject.parseObject(requestStr);
		if(StringUtils.isEmpty(jsonObj.getString("orderNo")) || StringUtils.isEmpty(jsonObj.getString("openId"))){
		   	throw new ParamException();
		}
	   	OrderInfo orderInfo = .....//此处写获取订单信息方法
	   	if(orderInfo == null){
	   		return AjaxUtil.renderFailMsg("订单不存在!");
	   	}else if(orderInfo.getPayAmount() == null || orderInfo.getPayAmount() <= 0){
	   		return AjaxUtil.renderFailMsg("订单有误,请确认!");
	   	}else if(orderInfo.getOrderStatus() != 1){//1待付款
	   		String msg = orderInfo.getOrderStatus() >1 ?"此订单已支付!":"订单未提交,请确认!";
	   		return AjaxUtil.renderFailMsg(msg);
	   	}else{
	   		logger.info("【小程序支付服务】请求订单编号:["+orderInfo.getOrderNo()+"]");
		   	Map<String, String> resMap = paymentService.xcxPayment(+orderInfo.getOrderNo(),orderInfo.getPayAmount(),jsonObj.getString("openId"));
		   	if("SUCCESS".equals(resMap.get("returnCode")) && "OK".equals(resMap.get("returnMsg"))){
		   		//统一下单成功
		   		resMap.remove("returnCode");
		   		resMap.remove("returnMsg");
		   		logger.info("【小程序支付服务】支付下单成功!");
		   		return AjaxUtil.renderSuccessMsg(resMap);
		   	}else{
		   		logger.info("【小程序支付服务】支付下单失败!原因:"+resMap.get("returnMsg"));
		   		return AjaxUtil.renderFailMsg(resMap.get("returnMsg"));
		   }
	   	}

	}
    /**
	 * <p>回调Api</p>
	 *
	 * @param request
	 * @param response
	 * @throws Exception
	 */
    @RequestMapping(value="xcxNotify")
	public void xcxNotify(HttpServletRequest request,HttpServletResponse response) throws Exception {
 		InputStream inputStream =  request.getInputStream();
 		//获取请求输入流
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while ((len=inputStream.read(buffer))!=-1){
            outputStream.write(buffer,0,len);
        }
        outputStream.close();
        inputStream.close();
        Map<String,Object> map = BeanToMap.getMapFromXML(new String(outputStream.toByteArray(),"utf-8"));
        logger.info("【小程序支付回调】 回调数据: \n"+map);
        String resXml = "";
        String returnCode = (String) map.get("return_code");
        if ("SUCCESS".equalsIgnoreCase(returnCode)) {
            String returnmsg = (String) map.get("result_code");
            if("SUCCESS".equals(returnmsg)){
            	//更新数据
            	int result = paymentService.xcxNotify(map);
            	if(result > 0){
	            	//支付成功
	                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
	                        + "<return_msg><![CDATA[OK]]></return_msg>"+"</xml>";
            	}
            }else{
                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                        + "<return_msg><![CDATA[报文为空]></return_msg>" + "</xml>";
                logger.info("支付失败:"+resXml);
            }
        }else{
            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[报文为空]></return_msg>" + "</xml>";
            logger.info("【订单支付失败】");
        }
        
        logger.info("【小程序支付回调响应】 响应内容:\n"+resXml);
        response.getWriter().print(resXml);
	}
}

Service接口层代码(部分代码)

/**
* <p>支付接口层</p>
* 
* @author att  
* @date 2018年5月27日
* @since jdk1.8
* @version 1.0
 */
public interface PaymentService {

	Map<String,String> xcxPayment(String orderNo, double money,String openId) throws Exception;
	
	int xcxNotify(Map<String,Object> map) throws Exception;
}

Service接口实现(部分代码):

@Service(value = "paymentService")
public class PaymentServiceImpl implements PaymentService{
	private static Logger LOGGER = LoggerFactory.getLogger(PaymentServiceImpl.class);
	
	@Value("${spring.profiles.active}")
	private String PROJECT_ENV;
	
	@Value("${hcc.wx.domain}")
	private String orderDomain;
	
	@Autowired
	private PaymentRecordMapper paymentRecordMapper;
	@Autowired
	private PaymentNotifyMapper paymentNotifyMapper;
	
	@Override
	public Map<String, String> xcxPayment(String orderNum, double money,String openId) throws Exception {
		LOGGER.info("【小程序支付】 统一下单开始, 订单编号="+orderNum);
		SortedMap<String, String> resultMap = new TreeMap<String, String>();
		//生成支付金额,开发环境处理支付金额数到0.01、0.02、0.03元
		double payAmount = PayUtil.getPayAmountByEnv(PROJECT_ENV, money);
		//添加或更新支付记录(参数跟进自己业务需求添加)
		int flag = this.addOrUpdatePaymentRecord(orderNum, payAmount,.....);
		if(flag < 0){
			resultMap.put("returnCode", "FAIL");
			resultMap.put("returnMsg", "此订单已支付!");
			LOGGER.info("【小程序支付】 此订单已支付!");
		}else if(flag == 0){
			resultMap.put("returnCode", "FAIL");
			resultMap.put("returnMsg", "支付记录生成或更新失败!");
			LOGGER.info("【小程序支付】 支付记录生成或更新失败!");
		}else{
	        Map<String,String> resMap = this.xcxUnifieldOrder(orderNum, PayConfig.TRADE_TYPE_JSAPI, payAmount,openId);
	        if(PayConstant.SUCCESS.equals(resMap.get("return_code")) && PayConstant.SUCCESS.equals(resMap.get("result_code"))){
	    		resultMap.put("appId", PayConfig.XCX_APP_ID);
	    		resultMap.put("timeStamp", PayUtil.getCurrentTimeStamp());
	    		resultMap.put("nonceStr", PayUtil.makeUUID(32));
	    		resultMap.put("package", "prepay_id="+resMap.get("prepay_id"));
	    		resultMap.put("signType", "MD5");
	    		resultMap.put("sign", PayUtil.createSign(resultMap,PayConfig.XCX_KEY));
	    		resultMap.put("returnCode", "SUCCESS");
	    		resultMap.put("returnMsg", "OK");
	    		LOGGER.info("【小程序支付】统一下单成功,返回参数:"+resultMap);
	        }else{
	        	resultMap.put("returnCode", resMap.get("return_code"));
	    		resultMap.put("returnMsg", resMap.get("return_msg"));
	    		LOGGER.info("【小程序支付】统一下单失败,失败原因:"+resMap.get("return_msg"));
	        }
		}
		return resultMap;
	}
	
	/**
	 * 小程序支付统一下单
	 */
	private Map<String,String> xcxUnifieldOrder(String orderNum,String tradeType, double payAmount,String openid) throws Exception{
    	//封装参数
        SortedMap<String,String> paramMap = new TreeMap<String,String>();
        paramMap.put("appid", PayConfig.XCX_APP_ID);
        paramMap.put("mch_id", PayConfig.XCX_MCH_ID);
        paramMap.put("nonce_str", PayUtil.makeUUID(32));
        paramMap.put("body", BaseConstants.PLATFORM_COMPANY_NAME);
        paramMap.put("out_trade_no", orderNum);
        paramMap.put("total_fee", PayUtil.moneyToIntegerStr(payAmount));
        paramMap.put("spbill_create_ip", PayUtil.getLocalIp());
        paramMap.put("notify_url", this.getNotifyUrl());
        paramMap.put("trade_type", tradeType);
        paramMap.put("openid",openid);
        paramMap.put("sign", PayUtil.createSign(paramMap,PayConfig.XCX_KEY));
        //转换为xml
        String xmlData = PayUtil.mapToXml(paramMap);
        //请求微信后台,获取预支付ID
        String resXml = HttpUtils.postData(PayConfig.WX_PAY_UNIFIED_ORDER, xmlData);
        LOGGER.info("【小程序支付】 统一下单响应:\n"+resXml);
        return PayUtil.xmlStrToMap(resXml);
    }
	
	private String getNotifyUrl(){
		//服务域名
		return PayConfig.PRO_SERVER_DOMAIN + "/wxapp/payment/xcxNotify";
	}
	
	/**
	 * 添加或更新支付记录
	 */
	@Override
	public int addOrUpdatePaymentRecord(String orderNo, double payAmount,......) throws Exception{
		//写自己的添加或更新支付记录的业务代码
		return 0;
	}
	
	@Override
	@Transactional(readOnly=false,rollbackFor={Exception.class})
	public int xcxNotify(Map<String,Object> map) throws Exception{
        int flag = 0;
        //支付订单编号
        String orderNo = (String)map.get("out_trade_no");
        //检验是否需要再次回调刷新数据
        //TODO 微信后台回调,刷新订单支付状态等相关业务

        return flag;
	}

PayUtil工具类:

/**
 * Function: 支付工具类 <br/>
 * date: 2018-01-18 <br/>
 *
 * @author att
 * @version 1.0
 * @since JDK1.8
 * @see
 */
public class PayUtil {

	static Logger log = LogManager.getLogger(PayUtil.class.getName());
	/**
	 * 获取当前机器的ip
	 *
	 * @return String
	 */
	public static String getLocalIp(){
		InetAddress ia=null;
		String localip = null;
        try {
            ia=ia.getLocalHost();
            localip=ia.getHostAddress();
        } catch (Exception e) {
            e.printStackTrace();
        }
		return localip;
        
	}
	
	/**
	 * Map转换为 Xml
	 * 
	 * @param data
	 * @return Xml
	 * @throws Exception
	 */
	public static String mapToXml(SortedMap<String, String> map) throws Exception {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        //防止XXE攻击
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);
        DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
        org.w3c.dom.Document document = documentBuilder.newDocument();
        org.w3c.dom.Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key: map.keySet()) {
            String value = map.get(key);
            if (value == null) {
                value = "";
            }
            value = value.trim();
            org.w3c.dom.Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString();
        try {
            writer.close();
        }
        catch (Exception ex) {
        }
        return output;
    }

	
	/**
	 * 创建签名Sign
	 * 
	 * @param key
	 * @param parameters
	 * @return
	 */ 
	public static String createSign(SortedMap<String,String> parameters,String key){  
        StringBuffer sb = new StringBuffer();  
        Set es = parameters.entrySet();
        Iterator<?> it = es.iterator();  
        while(it.hasNext()) {  
            Map.Entry entry = (Map.Entry)it.next();  
            String k = (String)entry.getKey();  
            if(entry.getValue() != null || !"".equals(entry.getValue())) {
            	String v = String.valueOf(entry.getValue());
            	if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
            		sb.append(k + "=" + v + "&");
            	}
            }  
        }  
        sb.append("key=" + key);  
        String sign = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();  
        return sign;  
    }
    
	
	/**
	 * XML转换为Map
	 * 
	 * @param strXML
	 * @return Map
	 * @throws Exception
	 */
	public static Map<String, Object> getMapFromXML(String strXML) throws Exception {
        try {
            Map<String, Object> data = new HashMap<String, Object>();
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            //防止XXE攻击
            documentBuilderFactory.setXIncludeAware(false);
            documentBuilderFactory.setExpandEntityReferences(false);
            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) {
                ex.printStackTrace();
            }
            return data;
        } catch (Exception ex) {
            throw ex;
        }
	}
    
	/**
	 * 生成随机数
	 * 
	 * @return
	 */
	public static String makeUUID(int len) {
		return UUID.randomUUID().toString().replaceAll("-", "").substring(0, len);
	}
	
	/**
     * 获取当前的Timestamp
     * 
     * @return
     */
    public static String getCurrentTimeStamp() {
        return Long.toString(System.currentTimeMillis()/1000);
    }

    /**
     * 获取当前的时间
     * @return
     */
    public static long getCurrentTimestampMs() {
        return System.currentTimeMillis();
    }
    
    /**
     * 生成订单号
     * 
     * @return
     */
    public static String generateOrderNo() {	
    	SimpleDateFormat sdf  = new SimpleDateFormat("yyMMdd");
        return sdf.format(new Date())+makeUUID(16);
    }
	
    /**
	 * 获取当前工程url
	 * 
	 * @param request
	 * @return
	 */
	public static String getCurrentUrl(HttpServletRequest request){
		return request.getScheme() +"://" + request.getServerName()  + ":" +request.getServerPort() +request.getContextPath();
	}
	
	/**
	 * Xml字符串转换为Map
	 * 
	 * @param xmlStr
	 * @return
	 */
	public static Map<String,String> xmlStrToMap(String xmlStr){
        Map<String,String> map = new HashMap<String,String>();
        Document doc;
        try {
            doc = DocumentHelper.parseText(xmlStr);
            Element root = doc.getRootElement();
            List children = root.elements();
            if(children != null && children.size() > 0) {
                for(int i = 0; i < children.size(); i++) {
                    Element child = (Element)children.get(i);
                    map.put(child.getName(), child.getTextTrim());
                }
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return map;
    }
	
	public static String getSceneInfo(String wapUrl,String name){
		Map<String,Map<String,String>> map = new HashMap<String, Map<String,String>>();
		if(!StringUtils.isEmpty(wapUrl) && !StringUtils.isEmpty(name)){
			/*{"h5_info": {"type":"Wap","wap_url": "https://pay.qq.com","wap_name": "腾讯充值"}}*/
			Map<String,String> childmap = new TreeMap<String, String>();
			childmap.put("type", "Wap");
			childmap.put("wap_url",wapUrl);
			childmap.put("wap_name", name);
			map.put("h5_info", childmap);
			return JSON.toJSONString(map);
		}
		return null;
	}
	

    /**
     * 转换金额型到整型
     * @param money
     * @return
     */
    public static String moneyToIntegerStr(Double money){
        BigDecimal decimal = new BigDecimal(money);
        int amount = decimal.multiply(new BigDecimal(100))
            .setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
        return String.valueOf(amount);
    }

    /** 
     * 除去数组中的空值和签名参数
     * @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 env
     * @param money
     * @param payType
     * @return
     */
    public static double getPayAmountByEnv(String env,Double money){
    	double pay_money = 0.01;
    	//测试环境
    	if(BaseConstants.PLATFORM_ENV_DEV.equals(env)){
			if(money>10000){
				pay_money = 0.03;
			}else if(money>1000){
				pay_money = 0.02;
			}else{
				pay_money = 0.01;
			}
			return pay_money;
    	}else{
    		//生成环境
    		return money;
    	}
    }
}

支付配置类:

/**
 * Function: 支付配置 <br/>
 * date: 2018-01-18 <br/>
 *
 * @author att
 * @version 1.0
 * @since JDK1.8
 */
public class PayConfig {

	//微信支付类型
	//NATIVE--原生支付
	//JSAPI--公众号支付-小程序支付
	//MWEB--H5支付
	//APP -- app支付
	public static final String TRADE_TYPE_NATIVE = "NATIVE";
	public static final String TRADE_TYPE_JSAPI = "JSAPI";
	public static final String TRADE_TYPE_MWEB = "MWEB";
	public static final String TRADE_TYPE_APP = "APP";
			
	//小程序支付参数
	public static String XCX_APP_ID;
	public static String XCX_MCH_ID;
	public static String XCX_KEY;
	
	//微信支付API
	public static final String WX_PAY_UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	
	//参数
	static{
		Properties properties = new Properties();
		try {
			properties.load(PayConstant.class.getClassLoader().getResourceAsStream("payment_config.properties"));
            //xcx
            XCX_APP_ID=(String) properties.get("xcx.pay.appid");
            XCX_MCH_ID=(String) properties.get("xcx.pay.mchid");
            XCX_KEY=(String) properties.get("xcx.pay.key");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Properties配置:

##config
xcx.pay.appid=wx**********
xcx.pay.mchid=*****
xcx.pay.key=**********

三,小程序端(获取统一下单返回参数发起支付)

在小程序端,发起支付请求到,Java后台的统一下单接口返回prepay_id等参数,然后封装调起微信的js方法:wx.requestPayment(OBJECT),具体参考文档:官方文档

测试一把:

本代码仅仅为项目中抽取的内容来写技术文章,如果想获取更完整内容或支持,请关注以下公众号,然后进入:关于我 >>> 联系我  联系本人。

2018-05-08 17:24:31 qq_39316096 阅读数 929
  • 微信小程序系统教程Java版[3/3阶段]_微信小程序支付

    微信小程序系统教程[初级阶段],微信小程序0基础学起,讲解微信小程序开发的基础知识。 微信小程序系统教程共有“微信小程序系统教程[初级阶段]”、“微信小程序系统教程[中级阶段]——核心技术”、“微信小程序系统教程[阶段]客服消息+微信支付+九宝电商系统”。 “微信小程序系统教程[阶段]全套课程”包含: 1.微信小程序系统教程[阶段]_客服消息 2.微信小程序系统教程[阶段]_微信支付 3.微信小程序系统教程[阶段]_九宝电商系统 学习“微信小程序系统教程[阶段]”要求有微信小程序的基础。建议先学习“微信小程序系统教程[初级阶段]”、“微信小程序系统教程[中级阶段]”,后在报名“微信小程序系统教程[阶段]”。 阶段讲解的客服消息,是针对小程序的。后台程序用接近底层的技术,没有使用三方技术。这样降低同学们学习成本。 微信支付,这部分课程很有难度,要求同学们认真听讲,有不会的技术问题可以请教老师。购买课程后请联系老师,提供单号,给你源程序。 九宝电商系统是一套十分适和学习、项目的课程。既可以系统的学习微信小程序相关知识,还可以修改后上线。 “微信小程序系统教程[中级阶段]——核心技术”重点讲解微信小程序事件、组件、API 微信小程序系统教程[初级阶段],微信小程序0基础学起,讲解微信小程序开发的基础知识。 购买课程的同学,可赠送就九宝老师编写的《微信小程序开发宝典》。 购课请咨询qq2326321088

    4263 人正在学习 去看看 翟东平
按照微信支付的流程来进行:
1.小程序调用后台接口生成预支付订单:
package com.stonedt.controller;


import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;


import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;


import com.alibaba.fastjson.JSON;
import com.stonedt.entity.HosOrder;
import com.stonedt.util.DateKit;
import com.stonedt.util.HttpClientsKit;
import com.stonedt.util.ResultUtil;
import com.stonedt.util.WxUtils;


public class Test {


	// 小程序appid
	public static final String appid = "";
	// app 密钥
	public static final String secret = "";
	// 微信支付的商户id
	public static final String mch_id = "";
	// 微信支付的商户密钥
	public static final String key = "";
	// 支付成功后的服务器回调url
	public static final String notify_url = "";
	// 签名方式,固定值
	public static final String SIGNTYPE = "MD5";
	// 交易类型,小程序支付的固定值为JSAPI
	public static final String trade_type = "JSAPI";
	// 微信统一下单接口地址
	public static final String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	// 微信统一下单接口地址
	public static final String body = "";
	// 交易类型 : 这里是微信小程序支付
	public static final String JSAPI = "JSAPI";


	/**
	 * 支付接口
	 * 
	 * @param openid  用户唯一标识
	 * @return
	 */
	@RequestMapping(value = "pay", produces = "text/json;charset=UTF-8")
	@ResponseBody
	public String pay(HttpServletRequest request,String openid) {


		// 商户订单号--->订单号唯一  
		//这里用时间戳模拟  商户订单号
		String out_trade_no = System.currentTimeMillis()+"";
		// 生成商户订单类      预支付信息组装    用于存入数据库 
		/**
		 * 这里省略 ....个人生成自己的 订单类存入数据
		 */
		// 生成随机串
		String nonce_str = WxUtils.getRandomStringByLength(32);
		// 获取用户后台IP地址(即使用微信小程序的手机IP)
		String spbill_create_ip = WxUtils.getIpAddr(request);


		// 组装参数,用户生成统一下单接口的签名
		Map<String, String> preParams = new HashMap<String, String>();
		
		//attach可以存储你想要在回调中获取的数据
		String attach = out_trade_no;


		preParams.put("appid", appid);
		preParams.put("attach", attach);
		preParams.put("mch_id", mch_id);
		preParams.put("nonce_str", nonce_str);
		preParams.put("body", body);
		preParams.put("out_trade_no", out_trade_no);
		preParams.put("total_fee", "1");// hospital.getHosPrice()
		preParams.put("spbill_create_ip", spbill_create_ip);// 用户ip
		preParams.put("notify_url", notify_url);
		preParams.put("trade_type", trade_type);
		preParams.put("openid", openid);


		// 参数按照参数名ASCII码从小到大排序拼接 (即key1=value1&key2=value2…)
		Map<String, String> cleanSignArray = WxUtils.cleanSignArray(preParams);
		String preSign = WxUtils.createLinkString(cleanSignArray);


		System.out.println(preSign);
		// MD5运算生成签名,这里是第一次签名,用于调用统一下单接口 --签名时 需要全部转换编码
		System.out.println(WxUtils.sign(preSign, key, "utf-8"));
		String fistSign = WxUtils.sign(preSign, key, "utf-8").toUpperCase(); // 问题商户密钥


		// 拼接统一下单接口使用的xml数据,和params存储的数据一样,并且排列顺序安装a.b.c.d...的从小到大顺序排列  要将上一步生成的签名fistSign一起拼接进去
		String xml = "<xml>" + "<appid><![CDATA[" + appid + "]]></appid>" + "<attach><![CDATA[" + attach
				+ "]]></attach>" + "<body><![CDATA[" + body + "]]></body>" + "<mch_id><![CDATA[" + mch_id
				+ "]]></mch_id>" + "<nonce_str><![CDATA[" + nonce_str + "]]></nonce_str>" + "<notify_url><![CDATA["
				+ notify_url + "]]></notify_url>" + "<openid><![CDATA[" + openid + "]]></openid>"
				+ "<out_trade_no><![CDATA[" + out_trade_no + "]]></out_trade_no>" + "<spbill_create_ip><![CDATA["
				+ spbill_create_ip + "]]></spbill_create_ip>" + "<total_fee><![CDATA[1]]></total_fee>"
				+ "<trade_type><![CDATA[" + JSAPI + "]]></trade_type>" + "<sign><![CDATA[" + fistSign + "]]></sign>"
				+ "</xml>";


		System.out.println("调试模式_统一下单接口 请求XML数据:" + xml);


		// 调用统一下单接口,并接受返回的结果 ---》发送请求时 参数需要转换统一编码
		String result = HttpClientsKit.httpRequest(pay_url, "POST", xml);


		System.out.println("调试模式_统一下单接口 返回XML数据:" + result);


		// 这里解析的是xml数据
		Map returnMap = WxUtils.doXMLParse(result);
		// 返回状态码
		String return_code = (String) returnMap.get("return_code");


		System.out.println("返回状态码" + return_code);
		// 返回给小程序端的结果
		Map<String, Object> respone = new HashMap<String, Object>();
		// 返回给小程序端需要的参数
		Map<String, String> data = new HashMap<String, String>();


		// 有返回结果
		if (return_code.equals("SUCCESS")) {
			// 业务结果
			String result_code = (String) returnMap.get("result_code");
			
			
			if (!"SUCCESS".equals(result_code)) {
				//业务结果为fail
				respone.put("code", 1);
				String err_code_des = (String) returnMap.get("err_code_des");
				respone.put("msg", err_code_des);
			}


			String prepay_id = (String) returnMap.get("prepay_id");
			data.put("appId", appid);
			data.put("timeStamp", System.currentTimeMillis() + "");
			data.put("nonceStr", "m9fil9bt27e49ag1jz54vtxffwci7e08");
			data.put("package", "prepay_id=" + prepay_id);
			data.put("signType", "MD5");


			// 拼接签名需要的参数
			// 参数按照参数名ASCII码从小到大排序拼接 (即key1=value1&key2=value2…)
			Map<String, String> signTemp = WxUtils.cleanSignArray(data);
			// 生成签名字符串
			String tempSign = WxUtils.createLinkString(signTemp);
			// 再次签名,这个签名用于小程序端调用wx.requesetPayment方法
			String resSign = WxUtils.sign(tempSign, key, "utf-8");
			data.put("paySign", resSign);


			respone.put("data", data);
			respone.put("code", 0);
			respone.put("msg", "success");
		} else {
			//返回结果失败
			respone.put("code", 1);
			String return_msg = (String) returnMap.get("return_msg");
			respone.put("msg", return_msg);
		}
		//返回的数据用于小 程序  调起支付  或 展示 错误信息
		return JSON.toJSONString(respone);
	}
}

2.小程序根据后台接口生成预支付订单后返回的数据生成预支付订单:
 
wx.requestPayment({
              timeStamp: e.data.data.timeStamp,
              nonceStr: e.data.data.nonceStr,
              package: e.data.data.package,
              signType: 'MD5',
              paySign: e.data.data.paySign,
              success: function (event) {
                // success     
                console.log(event);

                wx.showToast({
                  title: '支付成功',
                  icon: 'success',
                  duration: 2000
                });
                //处理  业务逻辑
                
              },
              fail: function (error) {
                // fail     
                console.log("支付失败")
                wx.showToast({
                  title: '支付失败',
                  icon: 'none',
                  duration: 2000
                });
              }
            })
3.后台的支付回调:

   
/**
	 * 微信通知后台的接口
	 * 
	 * @param request
	 * @return
	 */
	@RequestMapping(value = "notifi")
	@ResponseBody
	public void notifi(HttpServletRequest request, HttpServletResponse response) {
		/**
		 * 当收到通知进行处理时,应检查对应业务数据的状态,判断该通知是否已经处理过
		 * 涉及查询数据库 这里省略
		 * 直接默认未处理
		 */
		String resXml = "";
		try {
			// 通过ServletInputStream读取http请求传入的数据
			BufferedReader br = new BufferedReader(
					new InputStreamReader((ServletInputStream) request.getInputStream()));
			String temp = null;
			// 获取微信返回的xml
			StringBuffer wxRes = new StringBuffer();
			while ((temp = br.readLine()) != null) {
				wxRes.append(temp);
			}
			br.close();
			// 处理微信返回的xml数据
			String notityXml = wxRes.toString();
			System.out.println("接收到微信的返回值(notityXml):" + notityXml);
			Map map = WxUtils.doXMLParse(notityXml);
			// 判断返回结果
			String returnCode = (String) map.get("return_code");
			System.out.println("微信返回结果returnCode:" + returnCode);
			if ("SUCCESS".equals(returnCode)) {
				// 判断result_code--->交易是否成功需要查看result_code来判断
				String result_code = (String) map.get("result_code");
				System.out.println("微信业务返回结果result_code:" + result_code);
				if ("SUCCESS".equals(result_code)) {
					// 成功
					// 获取签名
					String notitySign = (String) map.get("sign");
					// 去除原sign
					map.remove("sign");

					// 对微信传回的数据进行2次签名验证
					if (WxUtils.verify(WxUtils.createLinkString(map), notitySign, key, "utf-8")) {
						// 修改订单信息 --根据订单号order_id
						/**
						 * 修改 根据out_trade_no(我们自己的订单号-->从微信返回的XML中获取 然后修改数据库的订单记录下面三项作为举例) 
						 * 
						 * 支付时间(pay_time) 
						 * 支付状态(order_status=1,支付成功)
						 * 微信流水号(wexin_serial_num)
						 * 
						 */
						// 获取参数
						// 微信支付订单号
						String wxOrderId = (String) map.get("transaction_id");
						// 商户订单号(我们自己的订单号)
						String out_trade_no = (String) map.get("out_trade_no");
						// 支付时间
						String payTime = (String) map.get("time_end");
						// 生成order类
						HosOrder order = new HosOrder();
						order.setOrderId(out_trade_no);
						order.setPayTime(DateKit.localDateTime2Date(payTime));
						order.setOrderStatus(1);
						order.setWexinSerialNum(wxOrderId);
						// 修改订单信息
						/**
						 * 根据serviceImpl  修改 对应订单数据
						 */

						// 通知微信服务器已经处理成功
						resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
								+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
					} else {
						//签名2次验证失败
						System.out.println("微信签名验证失败------");
						resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
								+ "<return_msg><![CDATA[签名验证失败!]]></return_msg>" + "</xml> ";
					}
				} else {
					//微信业务结果失败	
					String err_code_des = (String) map.get("err_code_des");
					System.out.println("微信业务结果失败,错误信息err_code_des:" + err_code_des);
					resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>" + "<return_msg><![CDATA["
							+ err_code_des + "]]></return_msg>" + "</xml> ";
				}

			}

			System.out.println("微信支付回调数据结束resXml:" + resXml);
			// 返回结果
			BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
			out.write(resXml.getBytes());
			out.flush();
			out.close();

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();

		}

	}
4.微信小程序支付涉及工具类:
package com.stonedt.util;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.Security;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
 

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;


/**
 * 微信支付工具类
 * @author spring
 *
 */
public class WxUtils {
	/**
	 * 把字符串最后拼接上 key ,再进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign
	 * @param text  需要签名的字符串
	 * @param key   密钥
	 * @param inpit_charset  编码格式 
	 * @return
	 */
	public static String sign(String text,String key,String inpit_charset) {
		String sign=text+"&key="+key;
		return DigestUtils.md5Hex(getContentBytes(sign, inpit_charset));
	}
	
	/**
	 * 验证签名是否正确
	 * @param text  需要签名的字符串 
	 * @param sign  微信的签名结果    
	 * @param key   密钥
	 * @param input_charset  编码格式 
	 * @return 验证结果
	 */
	public static boolean verify(String text, String sign, String key, String input_charset){     
        if(null==text||null==sign||null==key) {
        	return false;  
        }else {
        	text = text +"&key="+ key;     
            String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset)).toUpperCase();
            System.out.println("打印微信返回的数据组装的新的sign--mysign:"+mysign);
            if (mysign.equals(sign)) {     
                return true;     
            } else {     
                return false;     
            }  
        }
    } 
	
	
		
	/**
	 * 把字符串按照一点编码格式转成字节数组
	 * @param content  字符串
	 * @param charset  编码方式
	 * @return
	 */
	 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);     
	        }     
	    } 
	 
	 	
	 	/**   
	     * 除去数组中的空值和签名参数   
	     * @param sArray 签名参数组   
	     * @return 去掉空值与签名参数后的新签名参数组   
	     */     
	    public static Map<String, String> cleanSignArray(Map<String, String> signArray) {     
	        Map<String, String> result = new HashMap<String, String>();     
	        if (signArray == null || signArray.size() <= 0) {     
	            return result;     
	        }     
	        for (String key : signArray.keySet()) {     
	            String value = signArray.get(key);     
	            if (value == null || value.equals("")) {     
	                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是其中的静态方法,是用来对List类型进行排序,规则是从小到大
	        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;     
	    }  
	    
	    
	    /** 
	     * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。 
	     * @param strxml 
	     * @return 
	     * @throws JDOMException 
	     * @throws IOException 
	     */  
	    public static Map doXMLParse(String strxml){ 
	    	Map m = new HashMap();  
	    	try {
	        if(null == strxml || "".equals(strxml)) {  
	            return null;  
	        }  
	       
	        InputStream in = String2Inputstream(strxml);  
	        SAXBuilder builder = new SAXBuilder();  
	        Document doc;
			
				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();
	    	} catch (JDOMException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			} 
	    	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());  
	    }  
	    
	    
	    /** 
	     * 获取真实的ip地址 
	     * @param request 
	     * @return 
	     */  
	    public static String getIpAddr(HttpServletRequest request) {  
	        String ip = request.getHeader("X-Forwarded-For");  
	        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){  
	             //多次反向代理后会有多个ip值,第一个ip才是真实ip  
	            int index = ip.indexOf(",");  
	            if(index != -1){  
	                return ip.substring(0,index);  
	            }else{  
	                return ip;  
	            }  
	        }  
	        ip = request.getHeader("X-Real-IP");  
	        if(StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){  
	           return ip;  
	        }  
	        return request.getRemoteAddr();  
	    }
	    
	    /** 
	     * 获取一定长度的随机字符串,范围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();  
	       } 
	   
	    ////////////////下面是解密手机号的方法
	    
	    // 算法名
	    public static final String KEY_NAME = "AES";
	    // 加解密算法/模式/填充方式
	    // ECB模式只用密钥即可对数据进行加密解密,CBC模式需要添加一个iv
	    public static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";

	    /**
	     * 微信 数据解密<br/>
	     * 对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充
	     * 对称解密的目标密文:encrypted=Base64_Decode(encryptData)
	     * 对称解密秘钥:key = Base64_Decode(session_key),aeskey是16字节
	     * 对称解密算法初始向量:iv = Base64_Decode(iv),同样是16字节
	     *
	     * @param encrypted 目标密文
	     * @param session_key 会话ID
	     * @param iv 加密算法的初始向量
	     */
	    public static String wxDecrypt(String encrypted, String session_key, String iv) {
	        String json = null;
	        byte[] encrypted64 = Base64.decodeBase64(encrypted);
	        byte[] key64 = Base64.decodeBase64(session_key);
	        byte[] iv64 = Base64.decodeBase64(iv);
	        byte[] data;
	        try {
	            init();
	            json = new String(decrypt(encrypted64, key64, generateIV(iv64)));
	        } catch (Exception e) {
	            e.printStackTrace();
	        }
	        return json;
	    }

	    /**
	     * 初始化密钥
	     */
	    public static void init() throws Exception {
	        Security.addProvider(new BouncyCastleProvider());
	        KeyGenerator.getInstance(KEY_NAME).init(128);
	    }

	    /**
	     * 生成iv
	     */
	    public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
	        // iv 为一个 16 字节的数组,这里采用和 iOS 端一样的构造方法,数据全为0
	        // Arrays.fill(iv, (byte) 0x00);
	        AlgorithmParameters params = AlgorithmParameters.getInstance(KEY_NAME);
	        params.init(new IvParameterSpec(iv));
	        return params;
	    }

	    /**
	     * 生成解密
	     */
	    public static byte[] decrypt(byte[] encryptedData, byte[] keyBytes, AlgorithmParameters iv)
	            throws Exception {
	        Key key = new SecretKeySpec(keyBytes, KEY_NAME);
	        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
	        // 设置为解密模式
	        cipher.init(Cipher.DECRYPT_MODE, key, iv);
	        return cipher.doFinal(encryptedData);
	    }
}

微信返回的时间的格式问题(yyyyMMddHHmmss类型)

 /** 
     * yyyyMMddHHmmss 转换为Date 
     * @param localDateTime 
     */  
	public static Date localDateTime2Date( String datetime){ 
		//先把符合格式的字符串时间  转换成LocalDateTime
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");  
        LocalDateTime ldt = LocalDateTime.parse(datetime,dtf);  
        //把LocalDateTime 转换成Date
        ZoneId zoneId = ZoneId.systemDefault();  
        ZonedDateTime zdt = ldt.atZone(zoneId);//Combines this date-time with a time-zone to create a  ZonedDateTime.  
        Date date = Date.from(zdt.toInstant());  
        
        return date;
    }  

发送和接收 统一下单接口内容

 

/**   
     *   
     * @param requestUrl 请求地址   
     * @param requestMethod 请求方法   
     * @param outputStr 参数   
     */     
    public static String httpRequest(String requestUrl,String requestMethod,String outputStr){     
          
        StringBuffer buffer = null;     
        try{     
            URL url = new URL(requestUrl);     
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();     
            conn.setRequestMethod(requestMethod); 
            conn.setRequestProperty("Content-Type","text/xml;charset=UTF-8");
            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();  
    }  

最后感觉一位博主:https://blog.csdn.net/zhourenfei17/article/details/77765585

借鉴了不少,虽然有些地方出现了问题。。。。感谢!

2019-09-08 20:30:45 qq_41149326 阅读数 91
  • 微信小程序系统教程Java版[3/3阶段]_微信小程序支付

    微信小程序系统教程[初级阶段],微信小程序0基础学起,讲解微信小程序开发的基础知识。 微信小程序系统教程共有“微信小程序系统教程[初级阶段]”、“微信小程序系统教程[中级阶段]——核心技术”、“微信小程序系统教程[阶段]客服消息+微信支付+九宝电商系统”。 “微信小程序系统教程[阶段]全套课程”包含: 1.微信小程序系统教程[阶段]_客服消息 2.微信小程序系统教程[阶段]_微信支付 3.微信小程序系统教程[阶段]_九宝电商系统 学习“微信小程序系统教程[阶段]”要求有微信小程序的基础。建议先学习“微信小程序系统教程[初级阶段]”、“微信小程序系统教程[中级阶段]”,后在报名“微信小程序系统教程[阶段]”。 阶段讲解的客服消息,是针对小程序的。后台程序用接近底层的技术,没有使用三方技术。这样降低同学们学习成本。 微信支付,这部分课程很有难度,要求同学们认真听讲,有不会的技术问题可以请教老师。购买课程后请联系老师,提供单号,给你源程序。 九宝电商系统是一套十分适和学习、项目的课程。既可以系统的学习微信小程序相关知识,还可以修改后上线。 “微信小程序系统教程[中级阶段]——核心技术”重点讲解微信小程序事件、组件、API 微信小程序系统教程[初级阶段],微信小程序0基础学起,讲解微信小程序开发的基础知识。 购买课程的同学,可赠送就九宝老师编写的《微信小程序开发宝典》。 购课请咨询qq2326321088

    4263 人正在学习 去看看 翟东平

微信小程序学习

最近工作正好有闲暇时间,根据公司安排先前期学习调研一下“微信小程序”,以供后期解决相关的运营需求,而本篇文章就是我对此次学习的一次总结。
  • 什么是小程序 ?

  • 如何全面具体的学习?

  • 注册与新建小程序

  • 认识开发工具

  • 目录结构与代码构成

    • JSON 配置文件
    • WXML 模版文件
    • WXSS 样式文件
    • JS 脚本文件
  • 小程序的执行流程

  • 小程序知识体系导图

  • 小程序开发的重要说明

    • WXSS
    • 模版语法
    • 自定义模版
    • 数据绑定
    • 事件处理
    • 自定义组件
    • 行为 - Behaviors
    • 生命周期函数
    • 作用域
    • 多页面数据共享
    • 版本库兼容性
    • 用户权限
    • WXS
  • 开源项目参考

  • 下一步计划

什么是小程序 ?

个人认为在样式、功能、使用方式上接近传统APP并依赖于微信运行环境的 H5页面就是所谓的“微信小程序”。
微信小程序对比传统的 APP,它无需安装,在微信环境中可直接运行,并且依附于微信的生态圈,所以可以被更快速,方便,高效的推广。

如何全面具体的学习?

学习“微信小程序”,可以打开“微信公众平台·小程序”平台,里面有非常详细的讲解。

https://developers.weixin.qq.com/miniprogram/dev/

在这个页面中,你可以从“介绍”、“设计”、“开发”、“运营”、“数据”等各个方面对小程序进行一个全面整体的认知。

若你是位“开发者”,那么应该专注学习“设计”,“开发”这两个方面。在“设计”方面你将学会小程序的交互和更棒的用户体验方面。而在“开发”中你将从“简易教程”、“框架”、“组件”、“API”、“工具”等全方面学习如何开发一款小程序应用。

如果有其它疑问,也可以在 社区 中进行反馈交流。

认识开发工具

“微信小程序”的开发需要特定的开发工具,通过“微信开发者工具”我们可以编译、调试、预览、上传、发布小程序页面,同时还可以管理微信小程序的授权状态等。

下载 > 微信开发中工具

“微信开发者工具”分为 “beta版” 与 正式版。一般我们使用的都是正式版,而像Git,NPM 之类新功能目前只有“beta版“才支持。
“微信开发者工具”下载安装完成后,可以选择建立“小程序项目”,然后输入小程序的名称,路径以及 “appID”,然后点击“确定”即可。
“微信开发者工具”不仅可以开发小程序项目,还可以开发“插件”、“代码片段”等。
“微信开发者工具”主要有以下几个重要功能点:

  1. 模拟器:可以设置网络、分辨率、缩放以及选择设备类型。
  2. 编辑器:与传统的编辑器相同,进行代码的编辑。
  3. 调试器:浏览器控制台,与Chrome基本相同主要新增了 appData、WXML、sensor等小程序专属的选项。
  4. 编译、预览、调试、远程调试。
  5. 详情:可以进行“项目设置”,“域名信息”、“腾讯云状态”。
  6. 其它常用功能:社区、上传、缓存管理、场景值、腾讯云(提供了一套完整的小程序解决方案,方便新手练习)。
https://developers.weixin.qq.com/miniprogram/dev/devtools/debug.html (更多开发工具的介绍)

“微信开发者工具”常用快捷键:

键名 说明
Ctrl+B 编译项目(焦点在开发工具中)
Ctrl+R 编译项目(焦点不在开发工具中)
Ctrl+Shift+P 预览项目
Shift+Alt+F 格式化代码

目录结构与代码构成

下面是一个小程序完整的目录结构图 (请右击查看大图)。
在这里插入图片描述
通过观察上图的目录与文件,你会发现小程序的代码主要有以下几个文件类型构成:

  • .json
  • .WXML
  • .wxss
  • .js

JSON 配置文件
.json 文件是小程序的配置文件。
在小程序中配置文件按照应用范围又分为以下主要四种:

  • project.config.json
  • app.json
  • pages.json
  • component.json`

project.config.json
它是开发者工具和小程序项目的共同配置文件,它可以在多个开发者中维护统一的开发设置和开发环境。例如编辑器的颜色、ES6转ES5、上传代码时自动压缩、是否校验不合法域名、版本库等等。
当新的开发者下载了开发者工具后,只需要将团队中的 project.config.json 文件拷贝到本地小程序的根目录中即可。

https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html (更多关于project.config.json)

app.json
它是整个小程序应用的全局配置文件,可以设置小程序的页面、窗口、tab栏、网络超时、debug、插件、分包、多线程等。
其中常用到的字段是 pageswindow以及 tabBar
pages用于配置小程序的页面路径,其值是一个数组,数组中的第一个元素就是小程序的主页,并且在开发工具中新增一个路径时,编辑器会自动创建对应页面的目录以及文件。

{
  "pages":[
    "pages/index/index",
    "pages/logs/logs"
  ]
}

PS:路径中的文件名 “index” 与 “logs” 不要加扩展名,小程序的编译工具会自动寻找对应的文件类型进行处理。
window 用于配置窗口的样式以及标题等。

属性 说明 取值
navigationBarBackgroundColor 导航栏背景颜色 HEX
navigationBarTextStyle black / white
navigationBarTitleText 导航栏标题文字内容 text
backgroundColor 窗口的背景色 HEX

tabBar也是比较常用的配置,用于设置 tab栏的位置(上、下)以及按钮的文字,样式和 icon。

https://developers.weixin.qq.com/miniprogram/dev/framework/config.html#%E5%85%A8%E5%B1%80%E9%85%8D%E7%BD%AE (更多关于app.json)

pages.json
每个页面都有自己的 pages.json 文件。
pages.json 文件可以在全局配置 app.json 的基础上对页面进一步的进行设置。例如导航栏的颜色,标题文字等。
另外 pages.json 还可以通过 usingComponents 字段来声明当前页面引用了那些自定义组件。

https://developers.weixin.qq.com/miniprogram/dev/framework/config.html#%E9%A1%B5%E9%9D%A2%E9%85%8D%E7%BD%AE (更多pages.json)

componet.json
componet.json 是自定义组件的配置文件。

{
  "component": true,        // 自定义组件声明
  "usingComponents": {}     // 可选项,用于引用别的组件
}

WXML 模版文件

你可以将 WXML 等同于传统 WEB开发时用到的 html,他们都是用来描述页面的结构骨架,不同的是 HTML文件由一个个html标记(Tag)组成,而 WXML则是有与之类似的“组件”组成。
除了在结构特性上与 HTML一致外,WXML 文件还支持特定的模版语法,数据绑定,事件处理、自定义模版等等。
同样的 WXML也可以分为页面的 WXML文件,自定义模版的 WXML文件以及自定义组件中的 WXML文件。

WXSS 样式文件
wxss 具有大部分 CSS的功能,所以你可以像 CSS一样去编写小程序的样式文件。
对于整个小程序公用的样式,可以放置在 app.wxss 文件中,而对于页面专用的样式,则写到对应页面的 pages.wxss文件中,其次便是自定义组件也会含有自己的 component.wxss文件。

JS 脚本文件

在小程序的脚本文件中,我们可以处理事件,响应用户请求,获取接口数据,改变数据状态…并且小程序的脚本文件默认支持commonJS规范,可以直接通过 require() 来导入模块,module.exports 导出模块,就编码的角度而言,与我们普通编写脚本文件并没有什么太大的区别,如果非要说区别的话,就是小程序中的脚本可以调用小程序内置的一些API接口,例如授权,支付等。

同样的app.js用于存放全局的数据和方法,而 pages.js 则存放每个页面自己的数据和方法,对于组件中的脚本文件而言,它保存的则是组件自己的数据和方法

小程序的执行流程
在这里插入图片描述

当微信客户端打开一个小程序的时候,会将整个小程序的代码包下载到本地,然后通过读取 app.json文件获取页面的路径,并将第一条路径作为首页,并根据 app.json 中的配置来初始化小程序窗口的样式。

紧接着,微信客户端再将 app.js 装载进来,执行其中的 App() 方法,实例化一个小程序对象(整个微信小程序也只有一个这样的实例对象,并且全部页面共享该对象,你可以在每个页面使用 getApp()方法来获取)。

当小程序实例对象创建完成后,会触发生命周期中的 onLaunch() 函数,然后继续装载小程序中的每个页面(默认便是首页),在装载页面的时候,同样的会先装载 pages.json的代码,用来初始化页面与窗口的样式,然后再装载 WXML文件以及其使用到的资源(wxs,模版、组件)来生成页面结构,一切就绪再加载 wxss获取页面样式,最后再去装载 .js的脚本文件。

当最后的 .js文件也被装载进来后, 就会调用脚本中的Page()构造函数,还回一个页面的实例对象,页面实例对象创建好后,就会根据其中的 data 数据与 WXML文件中的内容 一起渲染出最终的页面,最后页面渲染完成后,并且在这一过程中,页面实例会根据不同时期的状态触发不同的页面生命周期函数。

明白小程序的基本执行流程,对后面了解小程序执行的生命周期非常有帮助。

小程序知识体系导图
在这里插入图片描述

小程序开发的重要说明

小程序开发还是很简单的,像一些 API接口以及组件的使用,在开发手册上都有详细列举,所以这里我就将我个人认为小程序中比较常用或者是比较重要(跟传统web开发有点区别)的地方单独记录下来。
WXSS
WXSS 是基于 CSS 改进而来的,所以大部分的 CSS写法也可以套用在 WXSS 上,这里主要对这两者重点的区别加以说明。

rpx
新增的相对单位,rpx 默认将屏幕划分为750个单位。
注意的是:px 在小程序中依然也可以用。

选择器
WXSS 支持的选择器相比 CSS要少一些,但是主流的:类、ID、元素,分组、伪类(after,before)、伪元素(:hover 这里表示元素选中)等选择器都支持。
模块化样式
支持 @import “path” 导入样式。

内嵌样式
小程序中组件支持通过 style 属性来内嵌内联样式,并且还可以通过表达式来接收绑定的数据。

<view style="font-size:{{fontSize}}pt"></view>

背景图
wxss 不支持本地图片(相对路径图片)作为背景图,对于需要内嵌在 wxss文件中的图片,请使用 base64 或者是网上的图片。
不过,image 组件支持本地图片

https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxss.html (更多关于WXSS)

模版语法
WXML文件也称之为“WXML模版”文件,它与 HTML文件结构相同,都是有许多结构标记组成,只是在小程序中,这些标记被称之为“组件”。
WXML文件与 HTML文件的不同在于WXML模版支持特定的模版语法,可以直接再模版中进行运算处理。

表达式
WXML支持通过 {{variant}} 表达式来获取对应页面脚本中 data 定义的数据。

#page.wxml
<view>{{names}}</view>
#page.js
Page({
    data:{
        names:"xiaoming"
    }
})

{{}} 表达式不仅可以读取变量的值,还支持一些常规的运算操作,比如“算数运算”、“三元运算”、“比较运算”、“字符串运算”等,另外还可以在表达式中定义数组、对象等类型的值。

{{a + b}} + {{c}}
{{flag ? true : false}}
{{length > 5}}
{{"hellow" + name}}
{{[zero, 1, 2, 3, 4]}}
{{for: a, bar: b}} | {{...obj1, ...obj2, e: 5}} | {{name,age}}

好啦就先写到这里啦,有点长

2018-10-16 09:41:09 shixiansen6535 阅读数 585
  • 微信小程序系统教程Java版[3/3阶段]_微信小程序支付

    微信小程序系统教程[初级阶段],微信小程序0基础学起,讲解微信小程序开发的基础知识。 微信小程序系统教程共有“微信小程序系统教程[初级阶段]”、“微信小程序系统教程[中级阶段]——核心技术”、“微信小程序系统教程[阶段]客服消息+微信支付+九宝电商系统”。 “微信小程序系统教程[阶段]全套课程”包含: 1.微信小程序系统教程[阶段]_客服消息 2.微信小程序系统教程[阶段]_微信支付 3.微信小程序系统教程[阶段]_九宝电商系统 学习“微信小程序系统教程[阶段]”要求有微信小程序的基础。建议先学习“微信小程序系统教程[初级阶段]”、“微信小程序系统教程[中级阶段]”,后在报名“微信小程序系统教程[阶段]”。 阶段讲解的客服消息,是针对小程序的。后台程序用接近底层的技术,没有使用三方技术。这样降低同学们学习成本。 微信支付,这部分课程很有难度,要求同学们认真听讲,有不会的技术问题可以请教老师。购买课程后请联系老师,提供单号,给你源程序。 九宝电商系统是一套十分适和学习、项目的课程。既可以系统的学习微信小程序相关知识,还可以修改后上线。 “微信小程序系统教程[中级阶段]——核心技术”重点讲解微信小程序事件、组件、API 微信小程序系统教程[初级阶段],微信小程序0基础学起,讲解微信小程序开发的基础知识。 购买课程的同学,可赠送就九宝老师编写的《微信小程序开发宝典》。 购课请咨询qq2326321088

    4263 人正在学习 去看看 翟东平

 

        开发一个微信小程序需要https域名以及服务器

        1. 进入微信公众平台https://mp.weixin.qq.com/注册微信小程序,并获得APPID和APPsecrete。

            首先你得注册你的小程序,填写小程序的内容,过程很简单,博主就不赘述了

    

        

2.下载微信开发者工具,在上面那个网站可以下载。

    微信开发者工具是专门写微信小程序前台页面的,有html/javaScript/css基础的朋友可以结合API直接实践了。具体开发内容我就不详细说明了。

 

     

  

     3.后台和服务器

        后台:因为博主在比较熟悉SSM框架,就使用了SSM框架的后台,开发工具Eclipse,有javaWeb开发经验的朋友这里就很容易了,至于大家使用SSH,还是直接使用servlet,或者其他后台,这就看个人所熟悉什么技术了。

        服务器:在阿里云买了个服务器(学生价9.9/月,腾讯云似乎更便宜),数据库使用的是mysql。 嫌安装麻烦的朋友在这里推荐OneinStack,一键帮你装好java+tomcat+mysql,好像就几块钱。

        如果是购买阿里云的服务器,注意一下:阿里云在外部还有一层保护,记得配置安全组规则,打开80,8080,3306,443,8443端口,博主被这里坑了很久,因为没有打开3306端口,一直连接不上服务器上的mysql。

        至于前台如何连接到后台,api中有,最简单的就是wx.request直接发起url请求(多看看别人小程序源码以及API)。

    

  4.https域名(挺麻烦,但是两到三天就能搞定)

    开发微信小程序需要SSL证书的域名,在这里推荐大家买个便宜的域名,博主购买的是top,在阿里云上买的,几块钱。

    接下来就是域名备案了,这个比较麻烦,但是是必须的,推荐阿里云备案系统,大概两三个工作日就搞定了,阿里云的工作人员态度都非常好。

    域名备案完成后,阿里云上有免费的SSL证书,申请成功后下载,并按照他们的提示进行配置,就大功告成啦!

    不要忘了域名解析要解析到自己的服务器上。

    还有最后一步,你必须登录微信公众平台,配置服务器域名,这样才允许在微信前台中使用该域名。

   5.接下来就可以安心搞开发了,博主在开发过程中看了大量其他小程序的源码和官方API,终于完成了这个毕业设计。
--------------------- 
作者:huige_666 
来源:CSDN 
原文:https://blog.csdn.net/huige_666/article/details/79640073?utm_source=copy 

没有更多推荐了,返回首页