2022-05-28  2022-05-28    9743 字   20 分钟

第十四章

支付宝支付

优就业.JAVA教研室

学习目标

目标1:掌握二维码生成插件qrious的使用

目标2:能够说出支付宝支付开发的整体思路

目标3:能够调用支付宝支付接口(预下单)生成支付二维码

目标4:能够调用支付宝支付接口(查询订单)查询支付状态

目标5:实现支付日志的生成与订单状态的修改

一、二维码

1 什么是二维码

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

二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的;在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图象输入设备或光电扫描设备自动识读以实现信息自动处理:它具有条码技术的一些共性:每种码制有其特定的字符集;每个字符占有一定的宽度;具有一定的校验功能等。同时还具有对不同行的信息自动识别功能、及处理图形旋转变化点。

2 二维码优势

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

3 二维码容错级别

L级(低) 7%的码字可以被恢复。

M级(中) 的码字的15%可以被恢复。

Q级(四分)的码字的25%可以被恢复。

H级(高) 的码字的30%可以被恢复。

4 二维码生成插件qrious

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

官网地址:https://github.com/neocotic/qrious

qrious.js二维码插件的可用配置参数如下:

参数 类型 默认值 描述
background String “white” 二维码的背景颜色。
foreground String “black” 二维码的前景颜色。
level String “L” 二维码的误差校正级别(L, M, Q, H)。
mime String “image/png” 二维码输出为图片时的MIME类型。
size Number 100 二维码的尺寸,单位像素。
value String "" 需要编码为二维码的值

下面的代码即可生成一张二维码

<html>
<head>
<title>二维码入门小demo</title>
</head>
<body>
<img id="qrious">
<script src="qrious.min.js"></script>
<script>
 var qr = new QRious({
	   element:document.getElementById('qrious'),
	   size:250,
level:'H',	
value:'http://www.ujiuye.com'
	});
</script>
</body>
</html>

运行效果:

大家掏出手机,扫一下看看是否会看到优就业的官网呢?

二、支付宝扫码支付业务介绍及开发、环境配置流程

1 支付宝扫码支付业务流程

支付宝扫码支付是商户系统按支付宝支付协议生成支付二维码,用户再用支付宝“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景。

具体操作步骤:(了解)

第一步:创建应用

接入扫码支付能力,需要在开放平台创建一个应用,通过该应用来接入各种能力。

点击如下链接即可开始创建应用:https://openhome.alipay.com/platform/appManage.htm

第二步:添加应用功能

开发者在开发过程中,可以添加自己需要的功能到待申请功能列表。

给应用添加当面付功能,这样就可以在你的应用里使用扫码支付能力。

第三步:配置秘钥

为了保证交易双方的身份和数据安全,需要配置双方密钥。

第四步:沙箱环境调试使用

支付能力直接涉及到交易与资金,为了方便开放者调试支付能力,支付宝已经准备好沙箱环境,包括沙箱环境账号和沙箱版支付宝钱包,这样就可以在沙箱环境调试了。

第五步:签约

在正式使用这些能力的时候,需要在开放平台里进行签约,这时候约定的合同就生效了。也可以代替商户签约。

第六步:上线应用

上线:商户本身应用上线时候,也要把支付宝开放平台的应用上线。

验收:为了确保应用质量,开放平台提供了云验收平台,可以在线验收应用。

第七步:监控应用

在开放平台监控交易情况

应用上线后还可以在开放平台,查看应用运行情况以及交易状态。

2 扫码支付具体申请配置流程

2.1、登录支付宝开发者平台

打开支付宝官网:https://open.alipay.com

选择我是开发者

【注意】用自己的支付宝,扫描右边的扫码登录,在支付宝确认登录,即可登录支付宝开发者平台。初次登录进行身份验证

选择:自研开发者

同意协议,点击确定

2.2、登录开发者平台界面

选择研发服务

2.3、进入研发服务中心—》沙箱环境

沙箱环境(Beta)是协助开发者进行接口功能开发及主要功能联调的模拟环境。

作为开发者使用沙箱完全可以用来入门学习如何对接扫码支付接口。

Appid是应用的id,后续再开发应用时要用到,请保存好!

支付宝网关:https://openapi.alipaydev.com/gateway.do

2.4、配置沙箱环境,配置RSA2公钥

(1)鼠标移动到设置

处在弹出的文字介绍中,点击 生成方法

(2)根据操作系统进行选择进行下载。

在线生成:https://miniu.alipay.com/keytool/create

下载成功后,直接点击安装

安装成功

双击打开程序

依照图示选择

​ 密码长度为 RSA2

​ 密码格式为 PKCS8(Java适用)

然后点击生成密钥, 后 点击打开密钥文件路径

回到沙箱配置界面,点击 查看

点击更换应用公钥:

选择公钥,打开刚才我们生成的应用公钥,拷贝粘贴到文本框中

点击保存设置既可以把我们的公钥配置上去。

3 支付宝支付SDK

支付宝支付提供了SDK,使用支付宝支付SDK,在maven工程中引入依赖

<!-- 支付宝支付所需类库包 -->  
       <dependency>
            <groupId>com.alipay.sdk</groupId>
            <artifactId>alipay-sdk-java</artifactId>
            <version>4.3.0.ALL</version>
            <exclusions>
                <exclusion>
                    <groupId>org.bouncycastle</groupId>
                    <artifactId>bcprov-jdk15on</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

4 工程搭建与准备工作

(1)建立支付服务实现模块dongyimai-pay-service

(2)dongyimai-pay-service下创建application.yml,配置文件代码如下:

server:
  port: 9009
spring:
  application:
    name: pay
  redis:
    host: 192.168.188.128
    port: 6379
  main:
    allow-bean-definition-overriding: true
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:8761/eureka
  instance:
    prefer-ip-address: true
feign:
  hystrix:
    enabled: true
# 配置sql打印日志
logging:
  level:
    com:
      offcn: debug
#hystrix 配置
hystrix:
  command:
    default:
      execution:
        timeout:
          #如果enabled设置为false,则请求超时交给ribbon控制
          enabled: true
        isolation:
          strategy: SEMAPHORE

#支付宝支付信息配置
alipay:
  serverUrl: https://openapi.alipaydev.com/gateway.do
  appId: 2016102600767650
  privateKey: 用户私钥
  format: json
  charset: utf-8
  alipayPublicKey: 阿里公钥
  signType: RSA2

AlipayClient创建关键参数说明:

配置参数 示例值解释 获取方式/示例值
URL 支付宝网关(固定) https://openapi.alipay.com/gateway.do
APP_ID APPID即创建应用后生成 获取见上面创建应用并获取APPID
APP_PRIVATE_KEY 开发者应用私钥,由开发者自己生成 获取见上面配置密钥
FORMAT 参数返回格式,只支持json json(固定)
CHARSET 请求和签名使用的字符编码格式,支持GBK和UTF-8 开发者根据实际工程编码配置
ALIPAY_PUBLIC_KEY 支付宝公钥,由支付宝生成 获取详见上面配置密钥
SIGN_TYPE 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2 RSA2

接下来,就可以用alipayClient来调用具体的API了。alipayClient只需要初始化一次,后续调用不同的API都可以使用同一个alipayClient对象。

(4)在com.offcn.pay.config包下,创建AliPayConfig,代码如下:

@Configuration
public class AliPayConfig {
    @Value("${alipay.appId}")
    private String appId;
    @Value("${alipay.serverUrl}")
    private String serverUrl;
    @Value("${alipay.privateKey}")
    private String privateKey;
    @Value("${alipay.alipayPublicKey}")
    private String alipayPublicKey;
    @Value("${alipay.format}")
    private String format;
    @Value("${alipay.charset}")
    private String charset;
    @Value("${alipay.signType}")
    private String signType;
    @Bean
    public AlipayClient getPayClient() {
        return new DefaultAlipayClient(serverUrl, appId, privateKey, format, charset, alipayPublicKey, signType);
    }
}

(5)启动类创建

dongyimai-pay-service中创建com.offcn.pay.PayApplication,代码如下:

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableEurekaClient
public class PayApplication {

    public static void main(String[] args) {
        SpringApplication.run(PayApplication.class);
    }
}

pom.xml引入如下依赖

<dependencies>
    <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    <!-- 支付宝支付所需类库包 -->
    <dependency>
        <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java</artifactId>
        <version>4.3.0.ALL</version>
        <exclusions>
            <exclusion>
                <groupId>org.bouncycastle</groupId>
                <artifactId>bcprov-jdk15on</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>com.offcn</groupId>
        <artifactId>dongyimai-common</artifactId>
        <version>1.0</version>
    </dependency>
</dependencies>

三 东易买-支付宝支付二维码生成

1 需求分析与实现思路

1.1需求分析

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

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

1.2实现思路

商户系统通过AlipayClient调用支付宝预下单接口alipay.trade.precreate,获得该订单二维码图片地址。

构建参数发送给预下单接口 ,返回的信息中有支付url,根据url生成二维码,显示的订单号和金额也在返回的信息中。

重要入参说明

  • out_trade_no:商户订单号,需要保证商家系统不重复。
  • total_amount:订单金额。(单位:元)
  • subject:商品的标题/交易标题/订单标题/订单关键字等。不可使用特殊字符,如 /,=,& 等。
  • store_id:商户门店编号。
  • timeout_express:交易超时时间。

重要出参说明

  • qr_code:订单二维码(有效时间 2 小时)以字符串的格式返回,开发者需要自己使用工具根据内容生成二维码图片。

2 代码实现

2.1服务接口层

(1)在dongyimai-pay-service创建包com.offcn.pay.service ,包下建立接口

public interface AliPayService {
    /**
     * 生成支付宝支付二维码
     * @param out_trade_no 订单号
     * @param total_fee 金额(分)
     * @return
     */
    public Map createNative(String out_trade_no, String total_fee);

}

2.2服务实现层

dongyimai-pay-service创建com.offcn.pay.service.impl包,新建类

@Service
public class AliPayServiceImpl implements AliPayService {

    @Autowired
    private AlipayClient alipayClient;

    /**
     * 生成支付宝支付二维码
     *
     * @param out_trade_no 订单号
     * @param total_fee    金额(分)
     * @return
     */
    @Override
    public Map createNative(String out_trade_no, String total_fee) {
        Map<String, String> map = new HashMap<String, String>();
        //创建预下单请求对象
        AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
//转换下单金额按照元
        long total = Long.parseLong(total_fee);
        BigDecimal bigTotal = BigDecimal.valueOf(total);
        BigDecimal cs = BigDecimal.valueOf(100d);

        BigDecimal bigYuan = bigTotal.divide(cs);
        System.out.println("预下单金额:" + bigYuan.doubleValue());
        request.setBizContent("{" +
                "    \"out_trade_no\":\"" + out_trade_no + "\"," +
                "    \"total_amount\":\"" + bigYuan.doubleValue() + "\"," +
                "    \"subject\":\"测试购买商品001\"," +
                "    \"store_id\":\"xa_001\"," +
                "    \"timeout_express\":\"90m\"}");//设置业务参数
        //发出预下单业务请求
        try {
            AlipayTradePrecreateResponse response = alipayClient.execute(request);
            //从相应对象读取相应结果
            String code = response.getCode();
            System.out.println("响应码:" + code);
            //全部的响应结果
            String body = response.getBody();
            System.out.println("返回结果:" + body);

            if (code.equals("10000")) {
                map.put("qrcode", response.getQrCode());
                map.put("out_trade_no", response.getOutTradeNo());
                map.put("total_fee", total_fee);
                System.out.println("qrcode:" + response.getQrCode());
                System.out.println("out_trade_no:" + response.getOutTradeNo());
                System.out.println("total_fee:" + total_fee);
            } else {
                System.out.println("预下单接口调用失败:" + body);
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        return map;
    }

}

2.3控制层

dongyimai-pay-service创建com.offcn.pay.controller.PayController.java

@RestController
@RequestMapping("/pay")
public class PayController {


    @Autowired
    private AliPayService aliPayService;

    /**
     * 生成二维码
     *
     * @return
     */
    @GetMapping("/createNative")
    public Map createNative() {
        IdWorker idworker = new IdWorker();
        return aliPayService.createNative(idworker.nextId() + "", "1");
    }
}

这里我们订单号通过分布式ID生成器生成,金额暂时写死,后续开发我们再对接业务系统得到订单号和金额

浏览器测试 http://localhost:9009/pay/createNative

打开支付页面/pay.html,修改value路径,然后打开,会出现二维码,可以扫码试试

打开手机沙箱版支付宝,使用沙箱账号进行扫描。出现如下支付页面。

四 东易买-检测支付状态

1 需求分析及实现思路

1.1 需求分析

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

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

1.2 实现思路

我们通过AlipayClient实现对交易查询接口(alipay.trade.query)的调用。

交易查询接口具体参数:

关键入参:

参数名称 参数说明
out_trade_no 支付时传入的商户订单号,与trade_no必填一个
trade_no 支付时返回的支付宝交易号,与out_trade_no必填一个

关键出参:

参数名称 参数说明
trade_no 支付宝28位交易号
out_trade_no 支付时传入的商户订单号
trade_status 交易当前状态

我们在controller方法中轮询调用交易查询指定订单号(间隔3秒),当返回状态为success时,我们会在controller方法返回结果。前端代码收到结果后跳转到成功页面。

2 检测支付状态-后端代码

2.1 服务接口层

在dongyimai-pay-service的AliPayService.java中新增方法定义

	/**
	 * 查询支付状态
	 * @param out_trade_no
	 */
	public Map queryPayStatus(String out_trade_no);

2.2 服务实现层

在dongyimai-pay-service的AliPayServiceImpl.java中实现方法

/**
	 * 交易查询接口alipay.trade.query:
	 * 获取指定订单编号的,交易状态
	 * @throws AlipayApiException 
	 */
	@Override
	public  Map<String,String> queryPayStatus(String out_trade_no){
		Map<String,String> map=new HashMap<String, String>();
		AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
		request.setBizContent("{" +
				"    \"out_trade_no\":\""+out_trade_no+"\"," +
				"    \"trade_no\":\"\"}"); //设置业务参数
		//发出请求
		try {
			AlipayTradeQueryResponse response = alipayClient.execute(request);
			String code=response.getCode();
			System.out.println("返回值1:"+response.getBody());
			if(code.equals("10000")){
				//System.out.println("返回值2:"+response.getBody());
				map.put("out_trade_no", out_trade_no);
				map.put("tradestatus", response.getTradeStatus());
                 map.put("trade_no",response.getTradeNo());
			}			
		} catch (AlipayApiException e) {
			e.printStackTrace();
		}
		return map;
}

2.3 控制层

在dongyimai-pay-service的PayController.java新增方法

/**
 * 查询支付状态
 * @param out_trade_no
 * @return
 */
@RequestMapping("/queryPayStatus")
public Result queryPayStatus(String out_trade_no){
    Result result=null;
    while(true){
        //调用查询接口
        Map<String, String> map = null;
        try {
            map = aliPayService.queryPayStatus(out_trade_no);
        } catch (Exception e1) {
            /*e1.printStackTrace();*/
            System.out.println("调用查询服务出错");
        }
        if(map==null){//出错
            result=new  Result(false, StatusCode.ERROR,"支付出错");
            break;
        }
        if(map.get("tradestatus")!=null&&map.get("tradestatus").equals("TRADE_SUCCESS")){//如果成功
            result=new  Result(true,StatusCode.OK,"支付成功");
            break;
        }
        if(map.get("tradestatus")!=null&&map.get("tradestatus").equals("TRADE_CLOSED")){//如果成功
            result=new  Result(true, StatusCode.OK,"未付款交易超时关闭,或支付完成后全额退款");
            break;
        }
        if(map.get("tradestatus")!=null&&map.get("tradestatus").equals("TRADE_FINISHED")){//如果成功
            result=new  Result(true,StatusCode.OK, "交易结束,不可退款");
            break;
        }
        try {
            Thread.sleep(3000);//间隔三秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    return result;
}

3 查询时间限制

3.1问题分析

如果用户到了二维码页面一直未支付,或是关掉了支付页面,我们的代码会一直循环调用支付宝接口,这样会对程序造成很大的压力。所以我们要加一个时间限制或是循环次数限制,当超过时间或次数时,跳出循环。

3.2代码完善

(1)修改dongyimai-pay-service工程PayController.java的queryPayStatus方法

	@RequestMapping("/queryPayStatus")
	public Result queryPayStatus(String out_trade_no){
		Result result=null;		
		int x=0;		
		while(true){
			//调用查询接口
			.......		
			try {
				Thread.sleep(3000);//间隔三秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}	
			//为了不让循环无休止地运行,我们定义一个循环变量,如果这个变量超过了这个值则退出循环,设置时间为5分钟
			x++;
			if(x>=100){
				result=new  Result(false, StatusCode.ERROR, "二维码超时");
				break;
			}
		}
		return result;
	}

访问:http://localhost:9009/pay/queryPayStatus?out_trade_no=1394204936316682240,并扫描预下单时生成的二维码,进行支付。返回支付成功

长时间没有返回结果返回二维码超时:

五 支付日志

1 需求分析

我们现在系统还有个问题需要解决:系统中无法查询到支付记录,支付后订单状态没有改变

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

1.在用户下订单时,判断如果为支付宝支付,就向支付日志表添加一条记录,信息包括支付总金额、订单ID(多个)、用户ID 、下单时间等信息,支付状态为0(未支付)
2.生成的支付日志对象放入redis中,以用户ID作为key,这样在生成支付二维码时就可以从redis中提取支付日志对象中的金额和订单号。
3.当用户支付成功后,修改支付日志的支付状态为1(已支付),并记录支付宝传递给我们的交易流水号。根据订单ID(多个)修改订单的状态为2(已付款)。

2 表结构分析

tb_paylog 支付日志表

字段 类型 长度 含义
out_trade_no varchar 30 支付订单号
create_time datatime 创建时间
pay_time datatime 支付完成时间
total_fee bigint 支付金额(分)
transaction_id varchar 30 交易流水号
trade_state varchar 1 交易状态
pay_type varchar 1 支付类型:1:支付宝2:微信3:网银
order_list varchar 200 订单表ID串,用逗号分隔

3 逆向工程

拷贝逆向工程中的相关日志的pojo、Feign到dongyimai-order-service-api,拷贝 dao、service、controller到dongyimai-order-service中。

4 插入日志记录

修改dongyimai-order-service工程com.offcn.order.service.impl.OrderServiceImpl 的add方法。

内容:判断如果支付方式为支付宝支付,向数据库插入支付日志记录,并放入redis存储

完整代码如下:

@Autowired
private PayLogMapper payLogMapper;
/**
 * @author zdy
 * 增加Order
 * 金额校验:后台校验
 * @param order
 */
@Override
public void add(Order order){

    // 得到购物车数据
    List<Cart> cartList = (List<Cart>) redisTemplate.boundHashOps("cartList").get(order.getUserId());

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

    for (Cart cart : cartList) {
        long orderId = idWorker.nextId();
        System.out.println("sellerId:" + cart.getSellerId());
        System.out.println("orderId:" + orderId);
        Order tborder = new Order();// 新创建订单对象
        tborder.setOrderId(orderId);// 订单ID
        tborder.setUserId(order.getUserId());// 用户名
        tborder.setPaymentType(order.getPaymentType());// 支付类型
        tborder.setStatus("1");// 状态:未付款
        tborder.setCreateTime(new Date());// 订单创建日期
        tborder.setUpdateTime(new Date());// 订单更新日期
        tborder.setReceiverAreaName(order.getReceiverAreaName());// 地址
        tborder.setReceiverMobile(order.getReceiverMobile());// 手机号
        tborder.setReceiver(order.getReceiver());// 收货人
        tborder.setSourceType(order.getSourceType());// 订单来源
        tborder.setSellerId(cart.getSellerId());// 商家ID
        // 循环购物车明细
        double money = 0;
        for (OrderItem orderItem : cart.getOrderItemList()) {
            orderItem.setId(idWorker.nextId());
            orderItem.setOrderId(orderId);// 订单ID
            orderItem.setSellerId(cart.getSellerId());
            money += Double.parseDouble(orderItem.getTotalFee());// 金额累加
            System.out.println("orderItem.getId():"+orderItem.getId());
            //减少库存  调用goods 微服务的 feign 减少库存
            itemFeign.decrCount(order.getUserId());
            //保存订单明细到数据库中

            orderItemMapper.insert(orderItem);
        }

        orderIdList.add(orderId+"");//添加到订单列表
        total_money+=money;

        tborder.setPayment(money+"");
        orderMapper.insert(tborder);
    }

    //线上支付,记录订单
    if ("1".equals(order.getPaymentType())) {//如果是支付宝支付
        PayLog payLog = new PayLog();
        String outTradeNo = idWorker.nextId() + "";//支付订单号
        payLog.setOutTradeNo(outTradeNo);//支付订单号
        payLog.setCreateTime(new Date());//创建时间
        //订单号列表,逗号分隔
        String ids = orderIdList.toString()
                .replace("[", "")
                .replace("]", "")
                .replace(" ", "");
        payLog.setOrderList(ids);//订单号列表,逗号分隔
        payLog.setPayType(order.getPaymentType());//支付类型
        //把元转换成分
        System.out.println("合计金额:" + total_money);
        BigDecimal total_money1 = BigDecimal.valueOf(total_money);
        BigDecimal cj = BigDecimal.valueOf(100d);
        //高精度乘法 推荐使用
        BigDecimal bigDecimal = total_money1.multiply(cj);
        //低精度计算方式,不推荐使用
        double hj = total_money * 100;
        System.out.println("合计:" + hj);
        System.out.println("高精度处理:" + bigDecimal.toBigInteger().longValue());
        payLog.setTotalFee(bigDecimal.toBigInteger().longValue());

        payLog.setTradeState("0");//支付状态  0 未支付 1已经支付
        payLog.setUserId(order.getUserId());//用户ID
        payLogMapper.insert(payLog);//插入到支付日志表
        redisTemplate.boundHashOps("payLog").put(order.getUserId(), payLog);//放入缓存
    }
    //增加积分,调用用户微服务的userFeign 增加积分
    userFeign.addPoints(10);
    redisTemplate.boundHashOps("cartList").delete(order.getUserId());
}

修改PayLog.java中主键生成的方式为外界输入

5 读取支付日志

5.1服务接口层

在dongyimai-order-service 工程中创建PayLogService.java, 新增方法

    /**
     * 根据用户查询payLog
     * @param userId
     * @return
     */
    public PayLog searchPayLogFromRedis(String userId);

5.2服务实现层

dongyimai-order-service的PayLogServiceImpl.java实现方法


    /**
     * 根据用户查询payLog
     *
     * @param userId
     * @return
     */
    @Override
    public PayLog searchPayLogFromRedis(String userId) {
        return (PayLog) redisTemplate.boundHashOps("payLog").get(userId);
    }

5.3控制层PayLogController

/***
     * 查询用户的支付日志
     * @return
     */
    @ApiOperation(value = "根据登录用户从redis中查询支付日志",notes = "查询用户支付日志",tags = {"OrderController"})
    @GetMapping("/searchPayLogFromRedis")
    public Result<PayLog> searchPayLogFromRedis(String userId){
        PayLog payLog = orderService.searchPayLogFromRedis(userId);
        return new Result<PayLog>(true, StatusCode.OK,"查询成功",payLog) ;
    }

5.4修改PayLogFegin

@FeignClient(name="ORDER")
public interface PayLogFeign {

/**
     查询用户的支付日志
     @return
     */
    @GetMapping("/payLog/searchPayLogFromRedis")
    public Result<PayLog> searchPayLogFromRedis(@RequestParam("userId") String userId);
....

5.5引入依赖

在dongyimai-pay-service中引入dongyimai-order-service-api的依赖

<dependency>
    <groupId>com.offcn</groupId>
    <artifactId>dongyimai-order-service-api</artifactId>
    <version>1.0</version>
    <exclusions>
        <exclusion>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </exclusion>
    </exclusions>
</dependency>

5.6修改启动类

修改dongyimai-pay-service启动类 PayApplication.java

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableFeignClients(basePackages = {"com.offcn.order.feign"})
@EnableEurekaClient
public class PayApplication {

    public static void main(String[] args) {
        SpringApplication.run(PayApplication.class);
    }
}

5.7 修改控制器PayController

修改dongyimai-pay-service工程PayController.java的createNative方法

实现思路:调用获取支付日志对象的方法,得到订单号和金额

@Autowired
private PayLogFeign payLogFeign;

/**
     * 生成二维码
     *
     * @return
     */
    @GetMapping("/createNative")
    public Map createNative() {
        //获取用户名
        Map<String, String> userMap = tokenDecode.getUserInfo();
        String username = userMap.get("user_name");
        System.out.println("当前登录用户:"+username);
        //到redis查询支付日志
        Result<PayLog> payLogResult = payLogFeign.searchPayLogFromRedis(username);
        PayLog payLog = payLogResult.getData();
        //判断支付日志存在
        if(payLog!=null){
            return aliPayService.createNative(payLog.getOutTradeNo(),payLog.getTotalFee()+"");
        }else{
            return new HashMap();
        }
    }

6 修改订单状态

订单支付成功后,需要修改日志记录支付交易流水号并持久化到数据库,并且修改订单的支付状态,然后将Redis中的订单删除:

6.1服务接口层

修改com.offcn.order.service.OrderService,添加修改订单状态方法,代码如下:

/***
 * 根据订单ID修改订单状态
 * @param transactionid 交易流水号
 * @param orderId
 */
public void updateStatus(String out_trade_no,String transactionid);

6.2服务实现层

修改com.offcn.order.service.impl.OrderServiceImpl,添加修改订单状态实现方法,代码如下:

/**
 * 修改订单状态
 *
 * @param out_trade_no   支付订单号
 * @param transaction_id 支付宝返回的交易流水号
 */
@Override
public void updateStatus(String out_trade_no, String transaction_id) {
    //1.修改支付日志状态
    PayLog payLog = payLogMapper.selectById(out_trade_no);
    payLog.setPayTime(new Date());
    payLog.setTradeState("1");//已支付
    payLog.setTransactionId(transaction_id);//交易号
    payLogMapper.updateById(payLog);
    //2.修改订单状态
    String orderList = payLog.getOrderList();//获取订单号列表
    String[] orderIds = orderList.split(",");//获取订单号数组

    for(String orderId:orderIds){
        Order order = orderMapper.selectById( Long.parseLong(orderId) );
        if(order!=null){
            order.setStatus("2");//已付款
            orderMapper.updateById(order);
        }
    }
    //3、清除redis缓存数据		
    redisTemplate.boundHashOps("payLog").delete(payLog.getUserId());

}

6.3控制层OrderController

 /**
 * 修改订单的状态
 * @param out_trade_no
 * @param transaction_id
 * @return
 */
@ApiOperation(value = "修改订单的状态",notes = "修改订单的状态",tags = {"OrderController"})
@RequestMapping(value="/updateOrderStatus",method = RequestMethod.GET)
public Result updateOrderStatus(
    @RequestParam(value="out_trade_no")  String out_trade_no, 
    @RequestParam(value="transaction_id") String transaction_id){
    try {
        orderService.updateStatus(out_trade_no,transaction_id);
        return new Result(true,StatusCode.OK,"修改成功");
    } catch (Exception e) {
        e.printStackTrace();
        return new Result(false,StatusCode.ERROR,"修改失败");
    }
}

6.4修改OrderFeign

/**
 * 修改订单状态
 * @param out_trade_no
 * @param transaction_id
 * @return
 */
@RequestMapping(value = "/updateOrderStatus",method = RequestMethod.GET)
public Result updateStatus(
    @RequestParam(value="out_trade_no")  String out_trade_no,  
    @RequestParam(value="transaction_id") String transaction_id);

6.5修改PayController

修改com.offcn.pay.controller.PayController中查询交易状态的方法queryPayStatus, 支付成功调用orderFeign修改订单的状态以及日志信息,代码如下:

                        if(map.get("tradestatus")!=null&&map.get("tradestatus").equals("TRADE_SUCCESS")){
    //如果成功
    result=new  Result(true,StatusCode.OK,"支付成功");
    orderFeign.updateStatus(map.get("out_trade_no"),map.get("trade_no"));
   break;
}

7.dongyimai-pay-service集成SpringSecurity环境及网关配置

7.1 pom.xml加入如下依赖

<dependency>
    <groupId>com.offcn</groupId>
    <artifactId>dongyimai-order-service-api</artifactId>
    <version>1.0</version>
    <exclusions>
        <exclusion>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<!--oauth依赖-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

7.2 引入公钥和验证文件

将public.key和文件ResourceServerConfig拷贝到dongyimai-pay-service中,参考其他项目

7.3 引入FeignInterceptor

在PayController引入FeignInterceptor

@Bean
public FeignInterceptor feignInterceptor(){
    return new FeignInterceptor();
}

7.4、修改网关配置,路由转发到支付微服务

application.yml

- id: dongyimai_pay_route #支付微服务
          uri: lb://PAY
          predicates:
            - Path=/api/pay/**
          filters:
            - StripPrefix=1

8.测试

8.1用户登录

8.2 添加购物车

http://localhost:8001/api/cart/addGoodsToCartList?itemId=1324601&num=5

8.3查询购物车

http://localhost:8001/api/cart/findCartList

8.4提交订单

http://localhost:8001/api/order/

8.5 查询redis

8.6查询数据库

8.7 预下单

http://localhost:8001/api/pay/createNative

8.8生成二维码

8.9 扫描支付并查询交易状态

8.10查看数据库:

订单状态也已经被修改为1

六 支付日志显示(学员实现)

需求:在运营商后台中,显示支付日志列表,实现按日期、状态、用户进行查询。

学员实现。


avatar
青山
悟已往之不谏 知来者之可追
一言
今日诗词
站点信息
本站访客数 :
本站总访问量 :