电商项目day18(微信支付)

今日目标:

二维码的简介

二维码的入门demo

微信平台支付接口调用

检测支付状态

支付日志

一.二维码简介以及入门demo

1.简介:

二维码又称 QR Code,QR 全称 Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的 Bar Code 条形码能存更多的信息,也能表示更多的数据类

2.优势

    信息容量大, 可以容纳多达 1850 个大写字母或 2710 个数字或 500 多个汉字
 应用范围广, 支持文字,声音,图片,指纹等等...
 容错能力强, 即使图片出现部分破损也能使用
 成本低, 容易制作

3.二维码的容错级别

L 级(低) 7%的码字可以被恢复。
M 级(中) 的码字的 15%可以被恢复。
Q 级(四分)的码字的 25%可以被恢复。
H 级(高) 的码字的 30%可以被恢复。

4.二维码的生成插件qrious

qrious 是一款基于 HTML5 Canvas 的纯 JS 二维码生成插件。通过 qrious.js 可以快速生成各种二维码,你可以控制二维码的尺寸颜色,还可以将生成的二维码进行 Base64 编码。

5.入门demo

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<script type="text/javascript" src="js/qrious.min.js"></script>
<body>
<img id="qrious">
</body>
<script type="text/javascript">
    var qr = window.qr = new QRious({
        element: document.getElementById('qrious'),
        size: 250,
        value: 'http://www.baidu.com'
    })
</script>
</html>

导入qrious的js插件

二.微信支付简介

微信扫码支付的申请步骤:

1.注册公众号(类型:服务号)

2.认证公众号

认证后才能申请  一次300

3.提交申请微信支付材料

登录公众平台,点击左侧菜单【微信支付】,开始填写资料等待审核,审核时间为 1-5个工作日内。

4.开户成功,登陆商户平台进行验证

资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。

5.在线签署协议

本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。

开发文档简介:

网址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5

我们介绍扫码支付:native

两种模式介绍:

第一种模式:微信平台返回支付的二维码 

详细的业务流程,查看维信开发文档

第二种模式:微信平台返回一个路径,我们在客户端,自己生成,二维码

同一下单api:

接口链接:

URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder

因为需要的参数很多,我们必须要封装的参数都要封装

注:参数值用XML转义即可,CDATA标签用于说明数据不被XML解析器解析。

查询订单:

应用场景:

该接口提供所有微信支付订单的查询,商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。

需要调用查询接口的情况:

  1. ◆ 当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知;
  2. ◆ 调用支付接口后,返回系统错误或未知交易状态情况;
  3. ◆ 调用付款码支付API,返回USERPAYING的状态;
  4. ◆ 调用关单或撤销接口API之前,需确认支付状态;

接口链接:

https://api.mch.weixin.qq.com/pay/orderquery

SDK安装:

维信需要xml格式的数据,我们可以通过维信提供的sdk 来转

所以我们可以组装成map格式的数据,然后通过通过SDK转化为xml格式的字符串

我们通过原生的HttpClient来发送请求

在这我们通过工具类来组装,底层我们封装

 

三.电商二维码生成

思路分析:我们首先封装,微信接口需要的数据,注意微信要的是xml格式的数据,我们必须通过微信的sdk将map格式转化为xml格式,然后通过httpClient发送请求,获得数据同样转化为map格式

后台代码:

@Service
@Transactional
public class PayServiceImpl implements PayService {

    //将需要的参数通过value值注入
    @Value("${appid}")
    private String appid;
    @Value("${partner}")
    private String partner;
    @Value("${partnerkey}")
    private String partnerkey;
    @Value("${notifyurl}")
    private String notifyurl;//回调地址
    @Override
    public Map<String, Object> createNative(String out_trade_no, String total_fee) throws Exception {

        //1.组装请求参数
        Map<String,String> paramMap = new HashMap<>();
        paramMap.put("appid",appid);
        paramMap.put("mch_id",partner);
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());//获得随机字符串
        paramMap.put("tbody","品优购");
        paramMap.put("tout_trade_no",out_trade_no);//订单编号
        paramMap.put("total_fee",total_fee);//总费用
        paramMap.put("spbill_create_ip","127.0.0.1");//本地ip
        paramMap.put("tnotify_url",notifyurl);//通知地址
        paramMap.put("trade_type","Native");//支付类型
        paramMap.put("product_id","1");//这个显示不是必须传的,但是native支付至必须传
        //将map格式的数据转换为xml数据格式
        String xmlParam = WXPayUtil.generateSignedXml(paramMap, partnerkey);
        //2.基于httpClient发送请求
        HttpClient httpClient = new HttpClient("URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder");
        httpClient.setHttps(true);
        httpClient.setXmlParam(xmlParam);//设置请求参数
        httpClient.post();
        //3.处理相应结果
        String content = httpClient.getContent();
        System.out.println(content);
        Map<String, String> resultMap = WXPayUtil.xmlToMap(content);//转为map数据
        //我们必须自己封装返回给前台
        Map<String,Object> map = new HashMap<>();
        map.put("code_url",resultMap.get("code_url"));
        map.put("out_trade_no",out_trade_no);
        map.put("total_fee",total_fee);
        
        return map;
    }

 controller层:

public class PayController {

    /**
     * 生成二维码
     */
    @Reference
    private PayService payService;
    @RequestMapping("/createNative")
    public Map<String,Object> createNative(){
        IdWorker idWorker = new IdWorker();
        try {
            //注意我们在这先是写死的id号
           return payService.createNative(idWorker.nextId()+"","1");
        } catch (Exception e) {
            e.printStackTrace();
            return new HashMap<>();
        }
    }
}

前台代码:

 //控制层 
app.controller('payController' ,function($scope,$controller   ,payService){
	
	$controller('baseController',{$scope:$scope});//继承

    //生成二维码
    $scope.createNative=function () {
        payService.createNative().success(function (response) {
            //接受后端传过来的支付订单号和支付金额
            $scope.out_trade_no=response.out_trade_no;
            $scope.total_fee=response.total_fee;

            //基于qrious生成二维码
            new QRious({
                element: document.getElementById('qrious'),
                size: 300,
                value: response.code_url,
                level:'H'
            })

        })
    }

});	

service层:

//服务层
app.service('payService',function($http){

    //读取列表数据绑定到表单中
    this.createNative=function(){
        return $http.get('pay/createNative.do');
    }

});

四.检测支付状态

注意:我们在实现过程中,在二维码生成的时候就继续调用  查询状态

 /**
     * 调用查询状态接口
     * @param out_trade_no
     * @return
     */
    @Override
    public Map queryPayStatus(String out_trade_no) throws Exception {
        //1.组装请求数据
        //1、组装请求参数
        Map<String,String> paramMap = new HashMap<>();
        paramMap.put("appid",appid);
        paramMap.put("mch_id",partner);
        paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
        paramMap.put("out_trade_no",out_trade_no);
        String xmlParam = WXPayUtil.generateSignedXml(paramMap, partnerkey);
        //2.发送HttpClient请求
        HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
        httpClient.setHttps(true);
        httpClient.setXmlParam(xmlParam);//设置请求参数
        httpClient.post();
        //3.返回相应数据
        String content = httpClient.getContent();
        System.out.println(content);
        Map<String, String> xmlMap = WXPayUtil.xmlToMap(content);
        return xmlMap;
    }
/**
     * 查询支付状态
     */
    @RequestMapping("/queryPayStatus")
    public Result queryPayStatus(String out_trade_no){
        try {

            int count=0;

            while (true){
                //每隔3秒调用一次
                Thread.sleep(3000);

                //超过5分钟,跳转循环(支付超时)
                count++;
                if(count>100){
                    return new Result(false,"timeout");
                }

                Map resultMap = payService.queryPayStatus(out_trade_no);
                //判断交易状态
                if(resultMap.get("trade_state").equals("SUCCESS")){

                    //支付成功后,更新订单和支付日志状态
                    //payService.updateStatus(out_trade_no, (String) resultMap.get("transaction_id"));


                    //支付成功
                    return new Result(true,"支付成功");
                }
            }


        } catch (Exception e) {
            e.printStackTrace();
            return new Result(false,"支付失败");
        }
    }
}

前台代码:

 //查询支付状态
    $scope.queryPayStatus=function () {
        payService.queryPayStatus($scope.out_trade_no).success(function (response) {
            if(response.success){
                //支付成功
                location.href="paysuccess.html#?money="+$scope.total_fee;
            }else {
                if(response.message=="timeout"){
                    //支付超时
                    $scope.createNative();
                }
                //支付失败
                location.href="payfail.html";
            }
        })
    }
    //获取支付金额
    $scope.getMoney=function () {
        $scope.money=$location.search()["money"];
    }
//查询支付状态
    this.queryPayStatus=function (out_trade_no) {
        return $http.get('pay/queryPayStatus.do?out_trade_no='+out_trade_no);
    }

注意:路由传参前面添加#号     

五.支付日志

需求分析以及思路介绍
        1、保存订单时,插入一条支付操作。(前提:在线支付)tb_pay_log  此时:订单和支付日志中的中的支付状态都是"未支付"
        
        
        2、当用户微信扫码支付成功后,修改订单和支付日志中的中的支付状态,为"已支付",修改支付时间为当前时间。
    
    tb_order tb_order_item
    
    
    tb_pay_log    支付日志表,记录支付行为
    
        以下字段,是保存订单时,记录一笔支付信息,需要操作的字段
          `out_trade_no` varchar(30) NOT NULL COMMENT '支付订单号',   //分布式存储 idWorker
          `create_time` datetime DEFAULT NULL COMMENT '创建日期',
          `total_fee` bigint(20) DEFAULT NULL COMMENT '支付金额(分)',
          `trade_state` varchar(1) DEFAULT NULL COMMENT '交易状态', //未支付状态
          `user_id` varchar(50) DEFAULT NULL COMMENT '用户ID',      
          `order_list` varchar(200) DEFAULT NULL COMMENT '订单编号列表',  //一笔支付可能对应多笔订单  1,2,3
          `pay_type` varchar(1) DEFAULT NULL COMMENT '支付类型',  //微信支付
        
        
        以下字段是微信支付成功后,需要更新的字段:
        `pay_time` datetime DEFAULT NULL COMMENT '支付完成时间',
        `transaction_id` varchar(30) DEFAULT NULL COMMENT '交易号码',  //微信返回,支付成功后需要更新的数据
        `trade_state` varchar(1) DEFAULT NULL COMMENT '交易状态',
    
    保存订单的同时,生成一笔支付。
        保存多个订单,也只生成一笔支付。
    
    
    在线支付时,保存支付日志
    
    读取支付日志,显示支付订单号和支付金额
    
    支付成功后,修改订单和支付日志状态
    `pay_time` datetime DEFAULT NULL COMMENT '支付完成时间',
    
    `trade_state` varchar(1) DEFAULT NULL COMMENT '交易状态',  已支付:2
     `transaction_id` varchar(30) DEFAULT NULL COMMENT '交易号码',

首先我们在保存订单是,插入一条支付操作,用户保存支付的所有信息

//如果是在线支付则,保存一笔订单
		if (order.getPaymentType().equals("1")){
        	//创建payLog对象
			TbPayLog payLog = new TbPayLog();
			/*	`out_trade_no` varchar(30) NOT NULL COMMENT '支付订单号',   //分布式存储 idWorker
		  `create_time` datetime DEFAULT NULL COMMENT '创建日期',
		  `total_fee` bigint(20) DEFAULT NULL COMMENT '支付金额(分)',
		  `trade_state` varchar(1) DEFAULT NULL COMMENT '交易状态', //未支付状态
		  `user_id` varchar(50) DEFAULT NULL COMMENT '用户ID',
		  `order_list` varchar(200) DEFAULT NULL COMMENT '订单编号列表',  //一笔支付可能对应多笔订单  1,2,3
		  `pay_type` varchar(1) DEFAULT NULL COMMENT '支付类型',  //微信支付*/
			payLog.setOutTradeNo(idWorker.nextId()+"");
			payLog.setCreateTime(new Date());
			payLog.setTotalFee((long)(totalMoney*100));//转化为分
			payLog.setTradeState("1");
			payLog.setUserId(order.getUserId());
			//[1 , 2 , 3]我们通过切割的方式
			payLog.setOrderList(ids.toString().replace("[","").replace("]","").replace(" ",""));
			payLog.setPayType("1");
			//保存
			payLogMapper.insert(payLog);
			//将日志保存redis中
			redisTemplate.boundHashOps("payLog").put(order.getUserId(),payLog);

交易成功后跟新支付日志

 @Autowired
    private TbPayLogMapper payLogMapper;
    @Autowired
    private TbOrderMapper orderMapper;
    /**
     * 跟新支付日志状态
     * @param out_trade_no
     * @param transaction_id
     */
    @Override
    public void updateStatus(String out_trade_no, String transaction_id) {
        //跟新日志状态
        TbPayLog payLog = payLogMapper.selectByPrimaryKey(out_trade_no);
        payLog.setPayTime(new Date());
        payLog.setTradeState("2");
        payLog.setTransactionId(transaction_id);
        payLogMapper.updateByPrimaryKey(payLog);

        //跟新订单状态

        String orderList = payLog.getOrderList();
        String[] split = orderList.split(",");
        for (String orderId : split) {
            TbOrder tbOrder = orderMapper.selectByPrimaryKey(Long.parseLong(orderId));
            tbOrder.setStatus("2");//已支付
            tbOrder.setPaymentTime(new Date());
        }
        //清除当前redis中关联支付日志的信息
        redisTemplate.boundHashOps("payLog").delete(payLog.getUserId());
    }

注意:最后一定把存在redis中的关联支付日志信息删除

从新打包,测试

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奋斗的小巍

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值