品优购项目记录: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),在支付成功调用该方法

 

阅读更多
博主设置当前文章不允许评论。

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