品优购项目记录:day18

今日目标:

(1)掌握二维码生成插件 qrious 的使用

(2)理解微信支付开发的整体思路

(3)调用微信支付接口(统一下单)生成支付二维码

(4)调用微信接口(查询订单)查询支付状态

(5)实现支付日志的生成与订单状态的修改

 

目录

1、工程搭建

1.1 建立支付服务接口工程(pay-interface)

1.2 建立支付服务实现工程(pay-service)

2、微信支付二维码生成

2.1 需求分析

2.2 后端

2.3 前端

3、检测支付状态

3.1 需求分析

3.2 后端

3.3 前端

3.4 二维码超时处理

3.5 支付成功显示支付金额

4、支付日志

4.1 需求分析

4.2 插入支付日志

4.3 读取支付日志

5、支付成功,修改订单状态


 

1、工程搭建

 

1.1 建立支付服务接口工程(pay-interface)

参考其他服务层接口

 

1.2 建立支付服务实现工程(pay-service)

(1)依赖pay-interface和common,其他依赖参考其他服务层工程

(2)添加微信支付 SDK 依赖

	<dependency>
		<groupId>com.github.wxpay</groupId>
		<artifactId>wxpay-sdk</artifactId>
		<version>0.0.3</version>
	</dependency>

(3)在common工程中放入HttpClient的工具类,并在pom.xml文件中引入依赖

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>	  		
</dependency>

(4)添加微信支付配置文件

(5)引入 qrious 的js文件到cart-web的plugins目录中

 

 

 

2、微信支付二维码生成

 

2.1 需求分析

在支付页面上生成支付二维码,并显示订单号和金额

用户拿出手机,打开微信扫描页面上的二维码,然后在微信中完成支付

 

2.2 后端

(1)服务层接口(pay-interface),新建WinxinPayService接口

package com.pinyougou.pay.service;

import java.util.Map;

/**
 * 微信支付接口
 * Author xushuai
 * Description
 */
public interface WeixinPayService {

    /**
     * 生成本地支付的数据信息
     *
     * @param out_trade_no 外部订单号
     * @param total_fee    金额,单位为:分
     * @return java.util.Map
     */
    Map createNative(String out_trade_no, String total_fee);
}

(2)服务层实现(pay-service),新建WinxinPayServiceImpl实现WinxinPayService

package com.pinyougou.pay.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.github.wxpay.sdk.WXPayUtil;
import com.pinyougou.pay.service.WeixinPayService;
import org.springframework.beans.factory.annotation.Value;
import util.HttpClient;

import java.util.HashMap;
import java.util.Map;

/**
 * 微信支付接口实现
 * Author xushuai
 * Description
 */
@Service
public class WeixinPayServieImpl implements WeixinPayService {

    @Value("${appid}")
    private String WEIXIN_APPID;
    @Value("${partner}")
    private String WEIXIN_PARTNER;
    @Value("${partnerkey}")
    private String WEIXIN_PARTNERKEY;
    @Value("${notifyurl}")
    private String WEIXIN_NOTIFYURL;

    @Override
    public Map createNative(String out_trade_no, String total_fee) {
        try {
            // 1.封装微信支付请求参数
            Map<String, String> param = buildParam(out_trade_no, total_fee);

            // 2.使用请求参数生成请求xml数据
            String signXml = WXPayUtil.generateSignedXml(param, WEIXIN_PARTNERKEY);
            System.out.println("xml数据:" + signXml);

            // 3.发送请求
            HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
            httpClient.setHttps(true);
            httpClient.setXmlParam(signXml);
            httpClient.post();

            // 4.获取请求响应结果
            String response = httpClient.getContent();
            Map<String, String> responseMap = WXPayUtil.xmlToMap(response);

            System.out.println("响应结果:" + responseMap);
            // 5.生成返回结果
            if (responseMap.get("return_code").equals("SUCCESS")) {
                Map resultMap = new HashMap();
                resultMap.put("code_url", responseMap.get("code_url"));// 二维码链接地址
                resultMap.put("out_trade_no", out_trade_no);// 订单号
                resultMap.put("total_fee", total_fee);// 金额

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

    /**
     * 构造微信支付请求参数
     *
     * @param out_trade_no 外部订单号
     * @param total_fee    金额
     */
    private Map<String, String> buildParam(String out_trade_no, String total_fee) {
        Map<String, String> param = new HashMap<>();
        // 公众账号ID
        param.put("appid", WEIXIN_APPID);
        // 商户号
        param.put("mch_id", WEIXIN_PARTNER);
        // 设备号(自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB")
        //param.put("device_info", "WEB");
        // 随机字符串(随机字符串,长度要求在32位以内。推荐随机数生成算法)
        param.put("nonce_str", WXPayUtil.generateNonceStr());
        // 商品描述
        param.put("body", "品优购");
        // 商品详情
        //param.put("detail", WEIXIN_APPID);
        // 商户订单号
        param.put("out_trade_no", out_trade_no);
        // 标价金额
        param.put("total_fee", total_fee);
        // 终端IP
        param.put("spbill_create_ip", "127.0.0.1");
        // 通知地址
        param.put("notify_url", "http://test.itcast.cn");
        // 交易类型
        param.put("trade_type", "NATIVE");

        return param;
    }
}

(3)控制层(cart-web),新建PayController

package com.pinyougou.cart.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.pinyougou.pay.service.WeixinPayService;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import util.IdWorker;

import java.util.Map;

/**
 * 支付控制层
 * Author xushuai
 * Description
 */
@RestController
@RequestMapping("/pay")
public class PayController {

    @Reference
    private WeixinPayService weixinPayService;

    /**
     * 生成二维码数据
     * 
     * @return java.util.Map
     */
    @RequestMapping("/createNative")
    public Map createNative(){
        IdWorker idWorker = new IdWorker();
        return weixinPayService.createNative(String.valueOf(idWorker.nextId()), "1");
    }
}

 

 

2.3 前端

(1)新建payService.js

app.service('payService',function($http){
	//本地支付
	this.createNative=function(){
		return $http.get('pay/createNative.do');
	}	
});

(2)新建payController.js 

app.controller('payController' ,function($scope ,payService){	
	//本地生成二维码
	$scope.createNative=function(){
		payService.createNative().success(
			function(response){
				$scope.money=  (response.total_fee/100).toFixed(2) ;	//金额
				$scope.out_trade_no= response.out_trade_no;//订单号
				//二维码
		    	var qr = new QRious({
		 		   element:document.getElementById('qrious'),
		 		   size:250,
		 		   level:'H',
		 		   value:response.code_url
		 		});				
			}
		);		
	}		
});

(3)在pay.html页面中引入JS文件和基础指令

(4)设置展示二维码的img标签的id为:qrious

(5)绑定变量,展示订单ID和订单总金额

 

 

 

3、检测支付状态

 

3.1 需求分析

当用户支付成功后跳转到成功页面

当返回异常时跳转到错误页面

 

 

3.2 后端

(1)服务层接口(pay-interface),在WinxinPayService新增方法

    /**
     * 查询订单支付状态
     *
     * @param out_trade_no 外部订单号
     * @return java.util.Map
     */
    Map queryPayStatus(String out_trade_no);

(2)服务层实现(pay-service),在WinxinPayServiceImpl新增实现

@Override
    public Map queryPayStatus(String out_trade_no) {
        try {
            // 1.封装请求参数
            Map<String, String> param = buildQueryPayStatusParam(out_trade_no);

            // 2.使用请求参数生成请求xml数据
            String signXml = WXPayUtil.generateSignedXml(param, WEIXIN_PARTNERKEY);
            System.out.println("xml数据:" + signXml);

            // 3.发送请求
            HttpClient httpClient = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
            httpClient.setHttps(true);
            httpClient.setXmlParam(signXml);
            httpClient.post();

            // 4.获取请求结果
            String response = httpClient.getContent();
            Map<String, String> responseMap = WXPayUtil.xmlToMap(response);
            System.out.println("响应结果:" + response);

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

    /**
     * 构造查询支付状态的请求参数
     *
     * @param out_trade_no 商户订单号
     * @return java.util.Map<java.lang.String,java.lang.String>
     */
    private Map<String, String> buildQueryPayStatusParam(String out_trade_no) {
        Map<String, String> param = new HashMap<>();
        // 公众账号ID
        param.put("appid", WEIXIN_APPID);
        // 商户号
        param.put("mch_id", WEIXIN_PARTNER);
        // 商户订单号
        param.put("out_trade_no", out_trade_no);
        // 随机字符串
        param.put("nonce_str", WXPayUtil.generateNonceStr());

        return param;
    }

(3)控制层(cart-web),在PayController中新增方法

    /**
     * 查询订单状态(三秒查询一次,直到支付成功)
     *
     * @param out_trade_no 商家订单号
     * @return entity.Result
     */
    @RequestMapping("/queryPayStatus")
    public Result queryPayStatus(String out_trade_no){
        Result result = null;
        while (true) {
            // 查询订单状态
            Map map = weixinPayService.queryPayStatus(out_trade_no);
            if (map == null) {
                result = Result.error("支付出错");
                break;
            }
            if (map.get("trade_state").equals("SUCCESS")) {
                result = Result.success("支付成功");
                break;
            }
            try {
                // 三秒查询一次订单状态
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return result;
    }

 

 

3.3 前端

(1)在payService.js中新增方法

    //查询支付状态
    this.queryPayStatus = function (out_trade_no) {
        return $http.get('pay/queryPayStatus.do?out_trade_no=' + out_trade_no);
    }

(2)在payController.js中新增方法

    //查询支付状态
    queryPayStatus = function (out_trade_no) {
        payService.queryPayStatus(out_trade_no).success(
            function (response) {
                if (response.success) {
                    location.href = "paysuccess.html";
                } else {
                    location.href = "payfail.html";
                }
            }
        );
    }

(3)在生成二维码成功响应后,执行查询订单状态的方法

 

 

3.4 二维码超时处理

(1)后端:在控制层中的PayController中的查询订单状态方法加入逻辑

(2)前端:当二维码已过期时,重新生成二维码

 

 

3.5 支付成功显示支付金额

(1)前端:修改payController中支付成功后的逻辑

(2)前端:在payController中引入$location 服务

 

(3)前端:在payController中新增方法

    //获取金额
    $scope.getMoney=function(){
        return $location.search()['money'];
    }

(4)前端:在paysuccess.html中引入相关js文件和基础指令

(5)前端:页面绑定变量展示支付金额

 

 

 

4、支付日志

 

4.1 需求分析

我们现在系统还有两个问题需要解决:

  1. 系统中无法查询到支付记录
  2. 支付后订单状态没有改变

我们现在就来解决这两个问题。

实现思路:

(1)在用户下订单时,判断如果为微信支付,就想支付日志表添加一条记录,信息包括支付总金额、订单ID(多个)、用户ID 、下单时间等信息,支付状态为0(未支付)

(2)生成的支付日志对象放入redis中,以用户ID作为key,这样在生成支付二维码时就可以从redis中提取支付日志对象中的金额和订单号。

(3)当用户支付成功后,修改支付日志的支付状态为1(已支付),并记录微信传递给我们的交易流水号。根据订单ID(多个)修改订单的状态为2(已付款)。

 

 

4.2 插入支付日志

(1)生成订单时,生成支付日志,修改OrderServiceImpl中的add方法(order-service)

    /**
     * 增加
     */
    @Override
    public void add(TbOrder order) {
        // 1.从redis中提取购物车列表
        List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(order.getUserId());

        List<String> orderIdList = new ArrayList();//订单ID列表
        double total_money = 0;//总金额 (元)

        // 2.循环购物车保存订单
        for (Cart cart : cartList) {
            // 生成订单对象
            TbOrder tbOrder = buildOrder(order);
            // 设置商家ID
            tbOrder.setSellerId(cart.getSellerId());
            // 合计金额
            double money = 0;

            // 循环购物车明细
            for (TbOrderItem orderItem : cart.getOrderItemList()) {
                // 补全订单明细数据
                orderItem.setOrderId(tbOrder.getOrderId());
                orderItem.setSellerId(cart.getSellerId());
                orderItem.setId(idWorker.nextId());
                // 保存
                orderItemMapper.insert(orderItem);

                // 累加合计金额
                money += orderItem.getTotalFee().doubleValue();
            }
            // 设置合计金额到订单
            tbOrder.setPayment(BigDecimal.valueOf(money));
            // 保存
            orderMapper.insert(tbOrder);

            // 添加到订单列表
            orderIdList.add(tbOrder.getOrderId() + "");
            // 累加到总金额
            total_money += money;
        }

        if ("1".equals(order.getPaymentType())) {//如果是微信支付
            TbPayLog payLog = new TbPayLog();
            String outTradeNo = idWorker.nextId() + "";//支付订单号
            payLog.setOutTradeNo(outTradeNo);//支付订单号
            payLog.setCreateTime(new Date());//创建时间
            //订单号列表,逗号分隔
            String ids = orderIdList.toString().replace("[", "").replace("]", "").replace(" ", "");
            payLog.setOrderList(ids);//订单号列表,逗号分隔
            payLog.setPayType("1");//支付类型
            payLog.setTotalFee((long) (total_money * 100));//总金额(分)
            payLog.setTradeState("0");//支付状态
            payLog.setUserId(order.getUserId());//用户ID
            payLogMapper.insert(payLog);//插入到支付日志表
            redisTemplate.boundHashOps("payLog").put(order.getUserId(), payLog);//放入缓存
        }

        // 3.清除购物车中的数据
        redisTemplate.boundHashOps("cartList").delete(order.getUserId());
    }

 

 

4.3 读取支付日志

(1)后端:服务层接口(order-interface),在OrderService中新增方法

	/**
	 * 获取指定用户的支付日志
	 *
	 * @param username 用户登录名
	 * @return com.pinyougou.pojo.TbPayLog
	 */
	TbPayLog searchPayLogFromRedis(String username);

(2)后端:服务层实现(order-service),在OrderServiceImpl中实现该方法

    @Override
    public TbPayLog searchPayLogFromRedis(String username) {
        return (TbPayLog) redisTemplate.boundHashOps("payLog").get(username);
    }

(3)后端:控制层(cart-web),修改PayController中的createNative方法的逻辑

    /**
     * 生成二维码数据
     * 
     * @return java.util.Map
     */
    @RequestMapping("/createNative")
    public Map createNative(){
        // 获取当前登录用户名
        String username = SecurityContextHolder.getContext().getAuthentication().getName();
        // 从缓存中取出支付日志
        TbPayLog tbPayLog = orderService.searchPayLogFromRedis(username);
        if (tbPayLog != null) {
            return weixinPayService.createNative(tbPayLog.getOutTradeNo(), String.valueOf(tbPayLog.getTotalFee()));
        }
        return new HashMap();
    }

 

 

 

5、支付成功,修改订单状态

(1)后端:服务层接口(order-interface),在OrderService中新增方法

    /**
     * 修改订单状态
     *
     * @param out_trade_no   支付订单号
     * @param transaction_id 微信返回的交易流水号
     */
    void updateOrderStatus(String out_trade_no, String transaction_id);

(2)后端:服务层实现(order-service),在OrderServiceImpl中实现该方法

    @Override
    public void updateOrderStatus(String out_trade_no, String transaction_id) {
        //1.修改支付日志状态
        TbPayLog payLog = payLogMapper.selectByPrimaryKey(out_trade_no);
        payLog.setPayTime(new Date());
        payLog.setTradeState("1");//已支付
        payLog.setTransactionId(transaction_id);//交易号
        payLogMapper.updateByPrimaryKey(payLog);
        //2.修改订单状态
        String orderList = payLog.getOrderList();//获取订单号列表
        String[] orderIds = orderList.split(",");//获取订单号数组

        for (String orderId : orderIds) {
            TbOrder order = orderMapper.selectByPrimaryKey(Long.parseLong(orderId));
            if (order != null) {
                order.setStatus(TbOrder.STATUS_PAY);//已付款
                orderMapper.updateByPrimaryKey(order);
            }
        }
        //清除redis缓存数据
        redisTemplate.boundHashOps("payLog").delete(payLog.getUserId());

    }

(3)后端:控制层(cart-web),在支付成功调用该方法

 

1.2. 结构化一下 1.3. 图形化一下 1.3.1. 运营商后台 1.3.2. 商家后台 1.3.3. 网页前台 参考京东 2. 技术选型 前端:angularJS + Bootstrap 后台:SSM( springmvc+spring+mybatis) 数据库:mysql,使用mycat读写分离 开发模式:SOA 服务中间件:dubbox,需要和zookeeper配合使用 注册中心:zookeeper 消息中间件:Activemq,使用spring-jms 负载均衡:nginx 搜索:solr集群(solrCloud),配合zookeeper搭建, 使用spring-data-solor 缓存:redis集群,使用spring-data-redis 图片存储:fastDFS集群 网页静态化:freemarker 单点登录:cas 权限管理:SpringSecurity, 跨域:cros 支付:微信扫描 短信验证:阿里大于 密码加密:BCrypt 富文本:KindEditor 事务:声明式事务 任务调度:spring task 所有的技术,都可能涉及到为什么用?怎么用?用的过程中有什么问题? 3. 框架搭建 3.1. 前端 理解baseControler.js、base.js、base_pagination.js,以及每一个xxxController.js里面都公共的做了些什么。 baseControler.js 分页配置 列表刷新 处理checkBox勾选 xxxControler.js 自动生成增删改查 base_pagination.js 带分页 base.js 不带分页 3.2. dao 使用了mybatis逆向工程 4. 模块开发 逐个模块开发就好 4.1. 学会评估模块难不难 一个模块难不难从几方面考虑。 涉及几张表? 1,2张表的操作还是没有什么难度的。 涉及哪些功能? 增删改查,批量删除。 前端展示? 分页列表、树形、面包屑、三级联动、内容格式化。 4.2. 举几个简单模块的例子 4.2.1. 品牌管理 单表 分页、新增、删除、修改 4.2.2. 规格管理 2张表 分页、新增、删除、修改、显示优化(显示列表内容的一部分) 4.2.3. 模板管理 2张表 分页、新增、删除、修改、显示优化(显示列表内容的一部分) 4.2.4. 分类管理 单表 4.2.5. 商家审核 单表 4.3. 举一个复杂模块 4.3.1. 商品新增 需要插入3张表,tb_goods、tb_goods_desc、tb_item 前端:三级联动、富文本、图片上传、动态生成内容 4.3.2. 商品修改 需要从3张表获取数据,然后进行回显。 4.4. 典型模块设计 4.4.1. 管理后台 商品新增、商品修改 4.4.2. 前台页面 搜索模块实现 购物车模块实现 支付模块实现 秒杀模块实现 5. 开发过程中问题&优化 1.1. 登录 单点登录怎么实现 session怎么共享 1.2. 缓存 哪些场景需要用到redis redis存储格式的选择 怎么提高redis缓存利用率 缓存如何同步 1.3. 图片上传 图片怎么存储 图片怎么上传 1.4. 搜索 ​ 怎么实现 数据量大、 并发量高的搜索 怎么分词 1.5. 消息通知 ​ 哪些情况用到activeMq 1.6. 优化 seo怎么优化 怎么加快访问速度 1.7. 秒杀 ​ 怎么处理高并发 ​ 秒杀过程中怎么控制库存
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值