【尚筹网】IDEA版实现(十五)支付

由于尚硅谷的视频是通过Eclipse软件来做的,其中有些操作与IDEA上有所区别,所以我在这里将IDEA区别于Eclipse的操作、操作过程中涉及的源码(与视频的源码略有出入)以及大家可能遇到的种种问题分享给大家,这些源码在我这里均是通过测试的,仅供参考!

1 模块配置

1.1 OrderVO类

新建member-entity\src\main\java\com\atguigu\crowd\entity\vo\OrderVO.java

package com.atguigu.crowd.entity.vo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class OrderVO implements Serializable {

    private static final long serialVersionUID = 1L;

    //    主键
    private Integer id;

    //    订单号
    private String orderNum;

    //    支付宝流水单号
    private String payOrderNum;

    //    订单金额
    private Double orderAmount;

    //    是否开发票
    private Integer invoice;

    //    发票抬头
    private String invoiceTitle;

    //    备注
    private String orderRemark;

    //    收货地址主键id
    private String addressId;

    //    订单与项目的信息
    private OrderProjectVO orderProjectVO;

}

1.2 Pom配置

修改pay-consumer\pom.xml

<dependencies>
    <!-- api工程的依赖 -->
    <dependency>
        <groupId>com.atguigu.crowd</groupId>
        <artifactId>atcrowdfunding17-member-api</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>

    <!-- 标配web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- SpringBoot单元测试 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

    <!-- thymeleaf模板引擎 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <!-- eureka客户端 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

    <!-- 引入springboot&redis整合场景 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!-- 引入springboot&springsession整合场景 -->
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java -->
    <dependency>
        <groupId>com.alipay.sdk</groupId>
        <artifactId>alipay-sdk-java</artifactId>
        <version>3.3.49.ALL</version>
    </dependency>

    <!-- 配置文件提示 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>

</dependencies>

1.3 主启动类&Application

新建order-consumer\src\main\java\com\atguigu\crowd\CrowdMainClass.java

package com.atguigu.crowd;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

// 启用Feign客户端功能
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class CrowdMainClass {

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

}

新建order-consumer\src\main\resources\application.yml

server:
  port: 7000
spring:
  application:
    name: atguigu-crowd-pay
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
  redis:
    host: 127.0.0.1
  session:
    store-type: redis

eureka:
  client:
    service-url:
      defaultZone: http://localhost:1000/eureka

ali:
  pay:
    alipay-public-key: 填写支付宝公钥
    app-id: 填写APPID
    charset: utf-8
    gateway-url: https://openapi.alipaydev.com/gateway.do
    merchant-private-key: 填写商家私钥
    notify-url: 填写内网穿透地址
    return-url: 填写返回
    sign-type: RSA2

1.4 支付配置类

新建pay-consumer\src\main\java\com\atguigu\crowd\config\PayProperties.java

package com.atguigu.crowd.config;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Component
@ConfigurationProperties(prefix = "ali.pay")
public class PayProperties {

    private String appId;
    private String merchantPrivateKey;
    private String alipayPublicKey;
    private String notifyUrl;
    private String returnUrl;
    private String signType;
    private String charset;
    private String gatewayUrl;

}

1.5 Zuul路由规则修改

修改member-zuul\src\main\resources\application.yml

zuul:
  ignored-services: "*"
  sensitive-headers: "*"        # 在Zuul向其他微服务重定向时保持原本头信息(请求头、响应头)
  routes:
    crowd-portal:
      service-id: atguigu-crowd-auth
      path: /**                 # 这里一定要使用两个“*”号,不然“/”路径后面的多层路径将无法访问
    crowd-project:
      service-id: atguigu-crowd-project
      path: /project/**         # 这里一定要使用两个“*”号,不然“/”路径后面的多层路径将无法访问
    crowd-order:
      service-id: atguigu-crowd-order
      path: /order/**
    crowd-pay:
      service-id: atguigu-crowd-pay
      path: /pay/**

2 支付功能

2.1 前端部分

修改order-consumer\src\main\resources\templates\confirm_order.html

<form id="summaryForm" action="pay/generate/order" method="post"></form>

<script>
    $("#payButton").click(function(){

        // 1.收集所有要提交的表单项的数据
        var addressId = $("[name=addressId]:checked").val();
        var invoice = $("[name=invoiceRadio]:checked").val();
        var invoiceTitle = $.trim($("[name=invoiceTitle]").val());
        var remark = $.trim($("[name=remark]").val());

        // 2.将上面收集到的表单数据填充到空表单中并提交
        $("#summaryForm")
            .append("<input type='hidden' name='addressId' value='"+addressId+"'/>")
            .append("<input type='hidden' name='invoice' value='"+invoice+"'/>")
            .append("<input type='hidden' name='invoiceTitle' value='"+invoiceTitle+"'/>")
            .append("<input type='hidden' name='orderRemark' value='"+remark+"'/>")
            .submit();

    });
    $("#knowRoleCheckBox").click(function(){
        var currentStatus = this.checked;
        if(currentStatus) {
            $("#payButton").prop("disabled", "");
        }else{
            $("#payButton").prop("disabled","disabled");
        }
    });
    $('#myTab a').click(function(e) {
        e.preventDefault()
        $(this).tab('show')
    })
</script>

2.2 后端部分

新建pay-consumer\src\main\java\com\atguigu\crowd\handler\PayHandler.java

package com.atguigu.crowd.handler;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.atguigu.crowd.api.MySQLRemoteService;
import com.atguigu.crowd.config.PayProperties;
import com.atguigu.crowd.entity.vo.OrderProjectVO;
import com.atguigu.crowd.entity.vo.OrderVO;
import com.atguigu.crowd.util.ResultEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.*;

@Controller
public class PayHandler {

    @Autowired
    private PayProperties payProperties;

    @Autowired
    private MySQLRemoteService mySQLRemoteService;

    private Logger logger = LoggerFactory.getLogger(PayHandler.class);

    @ResponseBody
    @RequestMapping("/generate/order")
    public String generateOrder(HttpSession session, OrderVO orderVO) throws AlipayApiException {

        // 1.从 Session 域获取 orderProjectVO 对象
        OrderProjectVO orderProjectVO = (OrderProjectVO) session.getAttribute("orderProjectVO");

        // 2.将 orderProjectVO 对象和 orderVO 对象组装到一起
        orderVO.setOrderProjectVO(orderProjectVO);

        // 3.生成订单号并设置到 orderVO 对象中
        String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); // 根据当前日期时间生成字符串
        String user = UUID.randomUUID().toString().replace("-", "").toUpperCase(); // 使用 UUID 生成用户 ID 部分
        String orderNum = time + user;
        orderVO.setOrderNum(orderNum);

        // 4.计算订单总金额并设置到 orderVO 对象中
        Double orderAmount = (double) (orderProjectVO.getSupportPrice() * orderProjectVO.getReturnCount() + orderProjectVO.getFreight());
        orderVO.setOrderAmount(orderAmount);

        // 将OrderVO对象存入Session域
        session.setAttribute("orderVO", orderVO);

        // 5.调用专门封装好的方法给支付宝接口发送请求
        return sendRequestToAliPay(orderNum, orderAmount, orderProjectVO.getProjectName(), orderProjectVO.getReturnContent());
    }

    /**
     * 为了调用支付宝接口专门封装的方法
     *
     * @param outTradeNo  外部订单号,也就是商户订单号,也就是我们生成的订单号
     * @param totalAmount 订单的总金额
     * @param subject     订单的标题,这里可以使用项目名称
     * @param body        商品的描述,这里可以使用回报描述
     * @throws AlipayApiException
     * @return 返回到页面上显示的支付宝登录界面
     */
    private String sendRequestToAliPay(String outTradeNo, Double totalAmount, String subject, String body) throws AlipayApiException {
        //获得初始化的AlipayClient
        AlipayClient alipayClient = new DefaultAlipayClient(
            payProperties.getGatewayUrl(),
            payProperties.getAppId(),
            payProperties.getMerchantPrivateKey(),
            "json",
            payProperties.getCharset(),
            payProperties.getAlipayPublicKey(),
            payProperties.getSignType());

        //设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(payProperties.getReturnUrl());
        alipayRequest.setNotifyUrl(payProperties.getNotifyUrl());

        alipayRequest.setBizContent("{\"out_trade_no\":\"" + outTradeNo + "\","
                                    + "\"total_amount\":\"" + totalAmount + "\","
                                    + "\"subject\":\"" + subject + "\","
                                    + "\"body\":\"" + body + "\","
                                    + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");

        //若想给BizContent增加其他可选请求参数,以增加自定义超时时间参数timeout_express来举例说明
        //alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\","
        //		+ "\"total_amount\":\""+ total_amount +"\","
        //		+ "\"subject\":\""+ subject +"\","
        //		+ "\"body\":\""+ body +"\","
        //		+ "\"timeout_express\":\"10m\","
        //		+ "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");
        //请求参数可查阅【电脑网站支付的API文档-alipay.trade.page.pay-请求参数】章节

        //请求
        return alipayClient.pageExecute(alipayRequest).getBody();

    }

    @ResponseBody
    @RequestMapping("/return")
    public String returnUrlMethod(HttpServletRequest request, HttpSession session) throws AlipayApiException,
    UnsupportedEncodingException {

        // 获取支付宝 GET 过来反馈信息
        Map<String,String> params = new HashMap<String,String>();
        Map<String,String[]> requestParams = request.getParameterMap();
        for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                    : valueStr + values[i] + ",";
            }
            // 乱码解决, 这段代码在出现乱码时使用
            valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        boolean signVerified = AlipaySignature.rsaCheckV1(
            params,
            payProperties.getAlipayPublicKey(),
            payProperties.getCharset(),
            payProperties.getSignType()); //调用 SDK 验证签名

        // ——请在这里编写您的程序(以下代码仅作参考)——
        if(signVerified) {
            // 商户订单号
            String orderNum = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");

            // 支付宝交易号
            String payOrderNum = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");

            // 付款金额
            String orderAmount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");

            // 保存到数据库
            // 1.从Session域中获取OrderVO对象
            OrderVO orderVO = (OrderVO) session.getAttribute("orderVO");

            // 2.将支付宝交易号设置到OrderVO对象中
            orderVO.setPayOrderNum(payOrderNum);

            // 3.发送给MySQL的远程接口
            ResultEntity<String> resultEntity = mySQLRemoteService.saveOrderRemote(orderVO);
            logger.info("Order save result="+resultEntity.getResult());

            return "trade_no:"+payOrderNum+"<br/>out_trade_no:"+orderNum+"<br/>total_amount:"+orderAmount;
        }else {

            // 页面显示信息:验签失败
            return "验签失败";
        }
    }

    @RequestMapping("/notify")
    public void notifyUrlMethod(HttpServletRequest request) throws UnsupportedEncodingException, AlipayApiException {
        //获取支付宝POST过来反馈信息
        Map<String,String> params = new HashMap<String,String>();
        Map<String,String[]> requestParams = request.getParameterMap();
        for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                    : valueStr + values[i] + ",";
            }
            //乱码解决,这段代码在出现乱码时使用
            valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }

        boolean signVerified = AlipaySignature.rsaCheckV1(
            params,
            payProperties.getAlipayPublicKey(),
            payProperties.getCharset(),
            payProperties.getSignType()); //调用SDK验证签名

        //——请在这里编写您的程序(以下代码仅作参考)——

        /* 实际验证过程建议商户务必添加以下校验:
1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号,
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额),
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email)
4、验证app_id是否为该商户本身。
*/
        if(signVerified) {//验证成功
            //商户订单号
            String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");

            //支付宝交易号
            String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");

            //交易状态
            String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");

            logger.info("out_trade_no="+out_trade_no);
            logger.info("trade_no="+trade_no);
            logger.info("trade_status="+trade_status);

        }else {//验证失败
            logger.info("验证失败");

        }
    }
}

修改member-api\src\main\java\com\atguigu\crowd\api\MySQLRemoteService.java

@RequestMapping("/save/order/remote")
ResultEntity<String> saveOrderRemote(@RequestBody OrderVO orderVO);

修改mysql-provider\src\main\java\com\atguigu\crowd\handler\OrderProviderHandler.java

@RequestMapping("/save/order/remote")
ResultEntity<String> saveOrderRemote(@RequestBody OrderVO orderVO) {

    try {
        orderService.saveOrder(orderVO);

        return ResultEntity.successWithoutData();

    } catch (Exception e) {
        e.printStackTrace();

        return ResultEntity.failed(e.getMessage());
    }

}

并新增saveOrder()方法及实现类:

修改mysql-provider\src\main\java\com\atguigu\crowd\service\impl\OrderServiceImpl.java

@Autowired
public OrderPOMapper orderPOMapper;

@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
@Override
public void saveOrder(OrderVO orderVO) {

    OrderPO orderPO = new OrderPO();

    BeanUtils.copyProperties(orderVO, orderPO);

    OrderProjectPO orderProjectPO = new OrderProjectPO();

    BeanUtils.copyProperties(orderVO.getOrderProjectVO(), orderProjectPO);

    // 保存orderPO自动生成的主键是orderProjectPO需要用到的外键
    orderPOMapper.insert(orderPO);

    // 从orderPO中获取orderId
    Integer id = orderPO.getId();

    // 将orderId设置到orderProjectPO
    orderProjectPO.setOrderId(id);

    orderProjectPOMapper.insert(orderProjectPO);
}

修改mysql-provider\src\main\resources\mybatis\mapper\OrderPOMapper.xml

<insert id="insert"
    parameterType="com.atguigu.crowd.entity.po.OrderPO"
    useGeneratedKeys="true" keyProperty="id">
    insert into t_order (id, order_num, pay_order_num,
                         order_amount, invoice, invoice_title,
                         order_remark, address_id)
    values (#{id,jdbcType=INTEGER}, #{orderNum,jdbcType=CHAR},
            #{payOrderNum,jdbcType=CHAR},
            #{orderAmount,jdbcType=DOUBLE}, #{invoice,jdbcType=INTEGER}, #{invoiceTitle,jdbcType=CHAR},
            #{orderRemark,jdbcType=CHAR}, #{addressId,jdbcType=CHAR})
    </insert>
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值