SSM项目————整合微信支付

我们这里只看支付,其他跟支付无关的东西不看

一、准备页面

主要有三个页面

1、第一个页面,购票页面

在这里插入图片描述

2、第二个页面,详情支付页面

紫色的三个是跟我们支付有关的

在这里插入图片描述

3、第三个页面,支付成功跳转页面

在这里插入图片描述

二、准备二维码(测试一下)

1、首先导入一个JS,二维码生成插件 qrious

在这里插入图片描述
2、创建一个页面,测试一下,注意一下,这个地址

在这里插入图片描述

三、准备微信参数、环境、HttpClient工具类、雪花算法工具类。

1、创建一个weixinpay.properties配置文件,配置微信支付需要的参数,这里的回调地址,我们没有用到。

在这里插入图片描述

2、在主XML配置文件加载我们的微信支付配置文件。

在这里插入图片描述
3、创建一个微信支付实体类,将我们的数据注入进去。

在这里插入图片描述
4、导入微信支付SDK,开发包。

在这里插入图片描述
5、导入我们的HttpClient工具类,后面调用微信支付接口会用到。

在这里插入图片描述
可能会跟其他的类重名,那就在后面加个Utils

在这里插入图片描述
6、加入雪花算法工具类,后面用来生成永远不会重复的订单编号。

在这里插入图片描述

四、开始支付功能编码(下单)

1、创建一个WeixinPayService,有两个参数。

在这里插入图片描述 2、编写WeixinPayServiceImpl实现类

package com.movie.service.impl;

import com.github.wxpay.sdk.WXPayUtil;
import com.movie.service.WeixinPayService;
import com.movie.utils.HttpClientUtils;
import com.movie.vo.WeiXin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import sun.net.www.http.HttpClient;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @BelongsProject: MovieSystem
 * @BelongsPackage: com.movie.service.impl
 * @Description: TODO
 */
@Service
public class WeixinPayServiceImpl implements WeixinPayService {

    @Resource
    WeiXin weiXin;

    /**
     * 调用微信接口获取支付的地址
     *
     * @param out_trade_no 订单号
     * @param total_fee    金额(分)
     * @return
     */
    @Override
    public Map createNative(String out_trade_no, String total_fee) {
        // 1.创建参数
        Map<String, String> param = new HashMap();// 创建参数
        //调用微信支付统一下单的接口,必须转的参数
        param.put("appid", weiXin.getAppid());// 公众号
        param.put("mch_id", weiXin.getPartner());// 商户号
        param.put("nonce_str", WXPayUtil.generateNonceStr());//调用工具类(微信SDK的)。随机字符串
        param.put("body", "北海市影城电影票");// 商品描述
        param.put("out_trade_no", out_trade_no);// 商户订单号
        param.put("total_fee", total_fee);// 总金额(以分为单位)
        param.put("spbill_create_ip", "127.0.0.1");// IP
        param.put("notify_url", "http://test.easyshop.cn");// 回调地址(随便写)
        param.put("trade_type", "NATIVE");// 交易类型,NATIVE类型表示扫码支持
        try {
            // 2.生成要发送的xml(将Map集合转为XML格式),因为微信要的是XML格式
            String xmlParam = WXPayUtil.generateSignedXml(param, weiXin.getPartnerkey());
            //使用HttpClient远程调用工具
            //https://api.mch.weixin.qq.com/pay/unifiedorder  这个是微信支付的接口
            HttpClientUtils client = new HttpClientUtils("https://api.mch.weixin.qq.com/pay/unifiedorder");
            client.setHttps(true);
            client.setXmlParam(xmlParam);
            client.post();
            // 3.获得结果
            // result是服务器返回的结果,是XML格式的
            String result = client.getContent();
            //将服务器返回的结果,XML转为Map
            Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
            //封装返回值
            Map<String, String> map = new HashMap<>();
            map.put("code_url", resultMap.get("code_url"));// 支付地址
            map.put("total_fee", total_fee);// 总金额
            map.put("out_trade_no", out_trade_no);// 订单号
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return new HashMap<>();
        }
    }
 }

注释都写得很清楚了,就不多做解释了,我们调用这个方法需要给它传递两个参数,一个是订单号、一个是总金额(就是我们微信扫码付款的金额)。最后创建一个Map,把支付地址,总金额,订单号返回。

在这里插入图片描述

3、编写BuyTicketController

package com.movie.web;

import com.movie.bean.Users;
import com.movie.service.BuyTicketService;
import com.movie.service.TicketService;
import com.movie.service.WeixinPayService;
import com.movie.utils.CommomResult;
import com.movie.utils.IdWorker;
import com.movie.vo.PlayVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Map;

@Controller
public class BuyTicketController {

    @Autowired
    BuyTicketService buyTicketService;

    @Autowired
    WeixinPayService weixinPayService;

    @Autowired
    TicketService ticketService;

    /**
     * 跳转支付页面,生成二维码
     *
     * @param play_id
     * @param seats
     * @return
     */
    @RequestMapping(value = "/buy_toPay", method = RequestMethod.POST)
    public String toPay(Integer play_id, String seats, Model model, HttpSession session) {

        //要购买的电影的座位号
        session.setAttribute("seats",seats);

        //根据电源id查找出电影的详细信息
        PlayVo vo = buyTicketService.findPlayVoById(play_id);

        //一张票的价格
        Double money = vo.getMoney();

        //以,进行分割,看看买了几张片
        StringBuffer stringBuffer = new StringBuffer();
        String[] arrays = seats.split(",");

        //待支付金额,总金额
        Double sumMoney = money * arrays.length;

        //循环遍历,加上排、座
        for (String s : arrays) {
            s = s.replace("-", "排") + "座";
            stringBuffer.append(s + " ");
        }
        model.addAttribute("seats", stringBuffer.toString());
        model.addAttribute("vo", vo);
        model.addAttribute("sumMoney", sumMoney);

        //2.生成一个永远不会重复的订单编号
        IdWorker worker = new IdWorker();
        long id = worker.nextId();
        String orderId = id+"";

        //调用微信接口,获取二维码支付地址
        Map<String, String> map = weixinPayService.createNative(orderId, "1"); //微信接口支付金额默认是以分为单位
        //微信要支付的地址,把这个地址生成二维码
        String code_url = map.get("code_url");
        model.addAttribute("code_url", code_url);
        //用户支付的订单号
        model.addAttribute("orderId", orderId);
        return "user_film_pay";
    }
}

注释都写得很清楚了,就不多做解释了,把得到的支付地址跟订单号存放在model,给它返回回去。

在这里插入图片描述
4、页面显示二维码

在这里插入图片描述

在这里插入图片描述

5、测试一下:

在这里插入图片描述

在这里插入图片描述
OK,没有问题

在这里插入图片描述

五、开始支付功能编码(查询订单状态)

我们扫码支付可以了,那么问题来了,我们扫码了之后,页面怎么知道它支付了还是没支付呢?

这就要查询订单的支付状态了,根据订单号查询订单的支付状态,看看它执行成功了还是失败。

1、在service下添加一个查询订单状态的方法

在这里插入图片描述

2、同上面一样,实现它

/**
     * 根据订单号查询订单的支付状态
     * @param out_trade_no
     * @return
     */
    @Override
    public Map queryPayStatus(String out_trade_no) {
        Map param = new HashMap();
        param.put("appid", weiXin.getAppid());// 公众账号ID
        param.put("mch_id", weiXin.getPartner());// 商户号
        param.put("out_trade_no", out_trade_no);// ----->订单号
        param.put("nonce_str", WXPayUtil.generateNonceStr());// 随机字符串
        //查询订单支付状态接口
        String url = "https://api.mch.weixin.qq.com/pay/orderquery";
        try {
            String xmlParam = WXPayUtil.generateSignedXml(param, weiXin.getPartnerkey());
            HttpClientUtils client = new HttpClientUtils(url);
            client.setHttps(true);
            client.setXmlParam(xmlParam);

            //发送POS请求
            client.post();

            //调用接口返回的结果
            String result = client.getContent();
            //把返回的XML文件格式转为Map
            Map<String, String> map = WXPayUtil.xmlToMap(result);

            System.out.println("---------------服务器端接口返回的支付结果:-----------------------");
            if(map!=null){
                Set<Map.Entry<String, String>> entries = map.entrySet();
                for (Map.Entry<String, String> entry : entries) {
                    System.out.println(entry.getKey()+"--->"+entry.getValue());
                }
            }

            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

传递一个订单号,返回Map结果

在这里插入图片描述
3、编写Controller,跟之前的Controller是同一个类

流程是这样的:
       我们传递一个订单号,会调用service去查询订单的状态,会返回一个Map。我们对Map进行判断,如果Map不为空,并且里面的 trade_state 的值为 SUCCESS ,则说明支付成功。
       如果Map为空,则说明支付异常,失败。
       那么还有一个问题,如果用户打开了页面,但是他就是不扫码支付呢?我们的请求只发送一次查询,是不是不合适啊?我们可以给它来一个线程,让它每3秒钟执行一次,来一个循环,时间为5分钟,如果5分钟内没有扫码,则支付超时。

/**
     * 根据订单号查询订单支付的状态--轮询订单支付状态接口
     *
     * @param out_trade_no
     * @return
     */
    @ResponseBody
    @RequestMapping("/pay_queryPayStatus")
    public CommomResult queryPayStatus(String out_trade_no,Integer play_id,HttpSession session) {
        CommomResult commonResult = null;
        try {
            //查询支付的订单号是:1320615983324332000
            System.out.println("查询支付的订单号是:"+out_trade_no);
            System.out.println("查询支付的订单play_id:"+play_id);
            String seats=(String)session.getAttribute("seats");// 3-4,5-6,7-8
            Users loginUser=(Users) session.getAttribute("loginUser");
            commonResult = null;
            int x = 0;
            while (true) {
                //查询订单支付状态
                Map map = weixinPayService.queryPayStatus(out_trade_no);
                if (map != null) {
                    if (map.get("trade_state").equals("SUCCESS")) {// 如果成功
                        //支付成功,把支付成功的数据,存入数据中,后期可以查看订单
                        String[] seatNumers = seats.split(",");
                        for (String seatName : seatNumers) {
                            int seat_id = ticketService.getSeatIdBySeatName(seatName);
                            //插入到订单表 ticket表中
                            ticketService.addTicket(out_trade_no,play_id,loginUser.getUser_id(),seat_id);
                        }
                        commonResult = new CommomResult(200, "支付成功");
                        break;
                    }
                }
                if (map == null) {
                    commonResult = new CommomResult(500, "支付出现异常!失败");
                    break;
                }
                try {
                    Thread.sleep(3000);//睡眠3秒钟,每3秒钟执行一次
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //为了不让循环无休止地运行,我们定义一个循环变量,如果这个变量超过了这个值则退出循环,设置时间为5分钟
                x++;
                if (x >= 60) {
                    commonResult = new CommomResult(502, "支付超时!");
                    break;
                }
            }
        } catch (Exception e) {
            commonResult = new CommomResult(500, "支付异常!");
            e.printStackTrace();
        } finally {
        }
        return commonResult;
    }

在这里插入图片描述
4、编写页面

我们什么时候开始调用Controller查询订单状态的方法呢?那当然是在页面一打开,二维码出来就开始调用了

在这里插入图片描述
5、启动测试一下:

等到5分钟没有支付,超时

在这里插入图片描述

点击确认,页面刷新,扫码支付

在这里插入图片描述

支付成功,跳到成功页面

在这里插入图片描述

————————
创作不易,如觉不错,随手点赞,关注,收藏(* ̄︶ ̄),谢谢~~
  • 14
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 19
    评论
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值