Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)二十二(下单和微信支付)

import com.leyou.order.interceptors.UserInterceptor;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.context.properties.EnableConfigurationProperties;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration

@EnableConfigurationProperties(JwtProperties.class)

public class MvcConfig implements WebMvcConfigurer {

@Autowired

private JwtProperties prop;

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new UserInterceptor(prop)).addPathPatterns(“/**”);

}

}

1.2.数据库设计


DROP TABLE IF EXISTS tb_order;

CREATE TABLE tb_order (

order_id bigint(20) NOT NULL COMMENT ‘订单id’,

total_pay bigint(20) NOT NULL COMMENT ‘总金额,单位为分’,

actual_pay bigint(20) NOT NULL COMMENT ‘实付金额。单位:分。如:20007,表示:200元7分’,

promotion_ids varchar(256) COLLATE utf8_bin DEFAULT ‘’,

payment_type tinyint(1) unsigned zerofill NOT NULL COMMENT ‘支付类型,1、在线支付,2、货到付款’,

post_fee bigint(20) NOT NULL COMMENT ‘邮费。单位:分。如:20007,表示:200元7分’,

create_time datetime DEFAULT NULL COMMENT ‘订单创建时间’,

shipping_name varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT ‘物流名称’,

shipping_code varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT ‘物流单号’,

user_id varchar(32) COLLATE utf8_bin NOT NULL COMMENT ‘用户id’,

buyer_message varchar(128) COLLATE utf8_bin DEFAULT NULL COMMENT ‘买家留言’,

buyer_nick varchar(32) COLLATE utf8_bin NOT NULL COMMENT ‘买家昵称’,

buyer_rate tinyint(1) DEFAULT NULL COMMENT ‘买家是否已经评价,0未评价,1已评价’,

receiver_state varchar(128) COLLATE utf8_bin DEFAULT ‘’ COMMENT ‘收获地址(省)’,

receiver_city varchar(256) COLLATE utf8_bin DEFAULT ‘’ COMMENT ‘收获地址(市)’,

receiver_district varchar(256) COLLATE utf8_bin DEFAULT ‘’ COMMENT ‘收获地址(区/县)’,

receiver_address varchar(256) COLLATE utf8_bin DEFAULT ‘’ COMMENT ‘收获地址(街道、住址等详细地址)’,

receiver_mobile varchar(11) COLLATE utf8_bin DEFAULT NULL COMMENT ‘收货人手机’,

receiver_zip varchar(16) COLLATE utf8_bin DEFAULT NULL COMMENT ‘收货人邮编’,

receiver varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT ‘收货人’,

invoice_type int(1) DEFAULT ‘0’ COMMENT ‘发票类型(0无发票1普通发票,2电子发票,3增值税发票)’,

source_type int(1) DEFAULT ‘2’ COMMENT ‘订单来源:1:app端,2:pc端,3:M端,4:微信端,5:手机qq端’,

PRIMARY KEY (order_id) USING BTREE,

KEY create_time (create_time) USING BTREE,

KEY buyer_nick (buyer_nick) USING BTREE

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin ROW_FORMAT=DYNAMIC;

DROP TABLE IF EXISTS tb_order_detail;

CREATE TABLE tb_order_detail (

id bigint(20) NOT NULL AUTO_INCREMENT COMMENT '订单详情id ',

order_id bigint(20) NOT NULL COMMENT ‘订单id’,

sku_id bigint(20) NOT NULL COMMENT ‘sku商品id’,

num int(11) NOT NULL COMMENT ‘购买数量’,

title varchar(256) NOT NULL COMMENT ‘商品标题’,

own_spec varchar(1024) DEFAULT ‘’ COMMENT ‘商品动态属性键值集’,

price bigint(20) NOT NULL COMMENT ‘价格,单位:分’,

image varchar(128) DEFAULT ‘’ COMMENT ‘商品图片’,

PRIMARY KEY (id) USING BTREE,

KEY key_order_id (order_id) USING BTREE

) ENGINE=InnoDB AUTO_INCREMENT=126 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT=‘订单详情表’;

DROP TABLE IF EXISTS tb_order_status;

CREATE TABLE tb_order_status (

order_id bigint(20) NOT NULL COMMENT ‘订单id’,

status int(1) DEFAULT NULL COMMENT ‘状态:1、未付款 2、已付款,未发货 3、已发货,未确认 4、交易成功 5、交易关闭 6、已评价’,

create_time datetime DEFAULT NULL COMMENT ‘订单创建时间’,

payment_time datetime DEFAULT NULL COMMENT ‘付款时间’,

consign_time datetime DEFAULT NULL COMMENT ‘发货时间’,

end_time datetime DEFAULT NULL COMMENT ‘交易完成时间’,

close_time datetime DEFAULT NULL COMMENT ‘交易关闭时间’,

comment_time datetime DEFAULT NULL COMMENT ‘评价时间’,

PRIMARY KEY (order_id) USING BTREE,

KEY status (status) USING BTREE

) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT=‘订单状态表’;

1.3.相关实体类


在这里插入图片描述

package com.leyou.order.pojo;

import lombok.Data;

import javax.persistence.Id;

import javax.persistence.Table;

import javax.persistence.Transient;

import java.util.Date;

import java.util.List;

@Data

@Table(name = “tb_order”)

public class Order {

@Id

private Long orderId;// id

private Long totalPay;// 总金额

private Long actualPay;// 实付金额

private Integer paymentType; // 支付类型,1、在线支付,2、货到付款

private String promotionIds; // 参与促销活动的id

private Long postFee = 0L;// 邮费

private Date createTime;// 创建时间

private String shippingName;// 物流名称

private String shippingCode;// 物流单号

private Long userId;// 用户id

private String buyerMessage;// 买家留言

private String buyerNick;// 买家昵称

private Boolean buyerRate;// 买家是否已经评价

private String receiver; // 收货人全名

private String receiverMobile; // 移动电话

private String receiverState; // 省份

private String receiverCity; // 城市

private String receiverDistrict; // 区/县

private String receiverAddress; // 收货地址,如:xx路xx号

private String receiverZip; // 邮政编码,如:310001

private Integer invoiceType = 0;// 发票类型,0无发票,1普通发票,2电子发票,3增值税发票

private Integer sourceType = 1;// 订单来源 1:app端,2:pc端,3:M端,4:微信端,5:手机qq端

@Transient

private OrderStatus orderStatus;

@Transient

private List orderDetails;

}

在这里插入图片描述

package com.leyou.order.pojo;

import lombok.Data;

import tk.mybatis.mapper.annotation.KeySql;

import javax.persistence.Id;

import javax.persistence.Table;

@Data

@Table(name = “tb_order_detail”)

public class OrderDetail {

@Id

@KeySql(useGeneratedKeys = true)

private Long id;

private Long orderId;// 订单id

private Long skuId;// 商品id

private Integer num;// 商品购买数量

private String title;// 商品标题

private Long price;// 商品单价

private String ownSpec;// 商品规格数据

private String image;// 图片

}

在这里插入图片描述

package com.leyou.order.pojo;

import lombok.Data;

import javax.persistence.Id;

import javax.persistence.Table;

import java.util.Date;

@Data

@Table(name = “tb_order_status”)

public class OrderStatus {

@Id

private Long orderId;

private Integer status;

private Date createTime;// 创建时间

private Date paymentTime;// 付款时间

private Date consignTime;// 发货时间

private Date endTime;// 交易结束时间

private Date closeTime;// 交易关闭时间

private Date commentTime;// 评价时间

}

1.4.相关数据传输对象DTO


在这里插入图片描述

在这里插入图片描述

package com.leyou.order.dto;

import lombok.AllArgsConstructor;

import lombok.Data;

import lombok.NoArgsConstructor;

@Data

@NoArgsConstructor

@AllArgsConstructor

public class CartDTO {

private Long skuId;//商品skuId

private Integer num;//购买数量

}

在这里插入图片描述

在这里插入图片描述

@Data

@AllArgsConstructor

@NoArgsConstructor

public class OrderDTO {//数据传输对象

@NonNull //@NonNull校验不能为空

private Long addressId;

@NonNull

private Integer paymentType;

@NonNull

private List carts;

}

1.5.相关Controller


在这里插入图片描述

在这里插入图片描述

package com.leyou.order.web;

import com.leyou.order.dto.OrderDTO;

import org.springframework.http.ResponseEntity;

import org.springframework.web.bind.annotation.*;

@RestController

@RequestMapping(“order”)

public class OrderController {

/*

创建订单

*/

@PostMapping

public ResponseEntity createOrder(@RequestBody OrderDTO orderDTO){

return null;

}

}

1.6.相关Service


在这里插入图片描述

在这里插入图片描述

package com.leyou.order.service;

import org.springframework.stereotype.Service;

@Service

public class OrderService {

}

1.7.相关Mapper


在这里插入图片描述

在这里插入图片描述

package com.leyou.order.mapper;

import com.leyou.common.mapper.BaseMapper;

import com.leyou.order.pojo.Order;

public interface OrderMapper extends BaseMapper {

}

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.mapper;

import com.leyou.common.mapper.BaseMapper;

import com.leyou.order.pojo.OrderDetail;

public interface OrderDetailMapper extends BaseMapper {

}

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.mapper;

import com.leyou.common.mapper.BaseMapper;

import com.leyou.order.pojo.OrderStatus;

public interface OrderStatusMapper extends BaseMapper {

}

1.8.完善OrderService


在这里插入图片描述

package com.leyou.order.service;

import com.leyou.order.mapper.OrderDetailMapper;

import com.leyou.order.mapper.OrderMapper;

import com.leyou.order.mapper.OrderStatusMapper;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

@Service

public class OrderService {

@Autowired

private OrderMapper orderMapper;

@Autowired

private OrderDetailMapper detailMapper;

@Autowired

private OrderStatusMapper statusMapper;

}

1.9.完善OrderController


在这里插入图片描述

2.0.实现orderService的createOrder方法


在这里插入图片描述

创建订单逻辑比较复杂,需要组装订单数据,基本步骤如下:

  • 获取登录用户信息

  • 生成订单编号,初始化订单基本信息

  • 查询收货人信息

  • 查询商品信息

  • 封装OrderDetail信息

  • 计算总金额、实付金额

  • 保存订单状态信息

  • 删除购物车中已购买商品减库存

2.0.1生成订单编号(雪花算法)

雪花算法是由Twitter公司开源的snowflake(雪花)算法。

  • 雪花算法的原理

雪花算法会生成一个64位的二进制数据,为一个Long型。

(转换成字符串后长度最多19),其基本结构:

在这里插入图片描述

第一位:为未使用

第二部分:41位为毫秒级时间(41位的长度可以使用69年)

第三部分:5位datacenterld和5位workerld(10位的长度最多支持部署1024个节点)

第四部分:最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

snowflake生成的ID整体上按照时间自增排序,

并且整个分布式系统内不会产生ID碰撞(由datacenterworkerld作区分),

并且效率较高。

经测试snowflake每秒能够产生26万个ID。

  • 配置

在这里插入图片描述

ly:

worker:

workerId : 1

dataCenterId : 1

  • 加载属性

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.config;

import lombok.Data;

import org.springframework.boot.context.properties.ConfigurationProperties;

@Data

@ConfigurationProperties(prefix = “ly.worker”)

public class IdWorkerProperties {

private long workerId;

private long dataCenterId;

}

  • 编写配置类

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.config;

import com.leyou.common.utils.IdWorker;

import org.springframework.boot.context.properties.EnableConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

@EnableConfigurationProperties(IdWorkerProperties.class)

public class IdWorkerConfig {

@Bean

public IdWorker idWorker(IdWorkerProperties prop){

return new IdWorker(prop.getWorkerId(),prop.getDataCenterId());

}

}

2.0.2 准备假物流数据

我们前端页面传来的是addressld,我们需要根据id查询物流信息,

但是因为还没做物流地址管理。

所以我们准备一些假数据。

首先是实体类:

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.dto;

import lombok.Data;

@Data

public class AddressDTO {

private Long id;

private String name;//收件人姓名

private String phone;//电话

private String state;//省份

private String city;//城市

private String district;//区

private String address;//街道地址

private String zipCode;//邮编

private Boolean isDefault;

}

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.client;

import com.leyou.order.dto.AddressDTO;

import java.util.ArrayList;

import java.util.List;

public abstract class AddressClient {

public static final List addressList = new ArrayList() {

{

AddressDTO address = new AddressDTO();

address.setId(1L);

address.setAddress(“太阳系”);

address.setCity(“银河系”);

address.setDistrict(“火星”);

address.setName(“大哥”);

address.setPhone(“15800000000”);

address.setState(“阿拉比亚大陆(Arabia Terra)”);

address.setZipCode(“210000” ) ;

address.setIsDefault(true) ;

add(address);

AddressDTO address2 = new AddressDTO();address2.setId(2L);

address.setAddress(“太阳系”);

address.setCity(“银河系”);

address2.setDistrict(“天王星”);

address2.setName(“张三”);

address2.setPhone(“13600000000” );

address2.setState(“北京”);

address2.setZipCode(“100000”);

address2.setIsDefault(false);

add(address2);

}

};

public static AddressDTO findById(Long id) {

for (AddressDTO addressDTO : addressList) {

if (addressDTO.getId() == id)

return addressDTO;

}

return null;

}

}

2.0.3 在ly-item的ly-item-interface当中新增通过sku的id集合查询其所有sku的接口

在这里插入图片描述

/*

通过sku的id集合查询其所有sku

*/

@GetMapping(“sku/list/ids”)

List querySkuBySpuId(@RequestParam(“ids”) List ids);

2.0.4 在Order当中定义对应client使用上述的接口

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.client;

import com.leyou.item.api.GoodsApi;

import org.springframework.cloud.openfeign.FeignClient;

@FeignClient(“item-service”)

public interface GoodsClient extends GoodsApi {

}

2.0.5 完善OrderService相关的工具类

2.0.5.1 定义异常枚举

在这里插入图片描述

CREATE_ORDER_ERROR(500,“创建订单失败”),

2.0.5.2 定义订单状态枚举

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.enums;

public enum OrderStatusEnum {

UN_PAY(1,“未付款”),

PAYED(2,“已付款,未发货”),

DELIVERED(3,“已发货,未确认”),

SUCCESS(4,“已确认,未评价”),

CLOSED(5,“已关闭,交易失败”),

RATED(6,“已评价”),

;

private int code;

private String desc;

OrderStatusEnum(int code, String desc) {

this.code = code;

this.desc = desc;

}

public int value(){

return this.code;

}

}

2.0.5.3 在StockMapper当中编写键库存的代码

在这里插入图片描述

public interface StockMapper extends BaseMapper{

@Update(“UPDATE tb_stock SET stock = stock - #{num} WHERE sku_id = #{id} AND stock >= #{num}”)

int decreaseStock(@Param(“id”) Long id, @Param(“num”) Integer num);

}

这里减库存并没有采用先查询库存,判断充足才减库存的方案,那样会有线程安全问题,当然可以通过加锁解决。不过我们此处为了效率,并没有使用悲观锁,而是对库存采用了乐观锁方案

2.0.5.4 在商品微服务当中定义减库存的内容
  • 在ly-common当中创建CartDTO,将下面order当中CartDTO复制一份到ly-common当中

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • GoodsController当中创建decreaseStock方法

在这里插入图片描述

/*

减库存

*/

@PostMapping(“stock/decrease”)

public ResponseEntity decreaseStock(@RequestBody List carts){

goodsService.decreaseStock(carts);

return ResponseEntity.status(HttpStatus.NO_CONTENT).build();

}

  • GoodsService

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

@Transactional

public void decreaseStock(List carts) {

for (CartDTO cart : carts) {

//键库存

int count = stockMapper.decreaseStock(cart.getSkuId(), cart.getNum());

if(count != 1){

throw new LyException(ExceptionEnum.STOCK_NOT_ENOUGH);

}

}

}

  • 测试

在这里插入图片描述

package com.leyou.item.service;

import com.leyou.common.dto.CartDTO;

import com.leyou.item.api.GoodsApi;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

@RunWith(SpringRunner.class)

@SpringBootTest

public class GoodsServiceTest {

@Autowired

private GoodsService goodsService;

@Test

public void decreaseStock(){

List cartDTOS =

Arrays.asList(

new CartDTO(2600242L, 2),

new CartDTO(2600248L, 2)

);

goodsService.decreaseStock(cartDTOS);

}

}

在这里插入图片描述

运行成功

在这里插入图片描述

在这里插入图片描述

2.0.5.5 将接口提供到API当中

在这里插入图片描述

/*

减库存

*/

@PostMapping(“stock/decrease”)

void decreaseStock(@RequestBody List carts);

2.0.6 完善OrderService的createOrder方法

在这里插入图片描述

package com.leyou.order.service;

import com.leyou.auth.entity.UserInfo;

import com.leyou.common.dto.CartDTO;

import com.leyou.common.enums.ExceptionEnum;

import com.leyou.common.exception.LyException;

import com.leyou.common.utils.IdWorker;

import com.leyou.item.pojo.Sku;

import com.leyou.order.client.AddressClient;

import com.leyou.order.client.GoodsClient;

import com.leyou.order.dto.AddressDTO;

import com.leyou.order.dto.OrderDTO;

import com.leyou.order.enums.OrderStatusEnum;

import com.leyou.order.interceptors.UserInterceptor;

import com.leyou.order.mapper.OrderDetailMapper;

import com.leyou.order.mapper.OrderMapper;

import com.leyou.order.mapper.OrderStatusMapper;

import com.leyou.order.pojo.Order;

import com.leyou.order.pojo.OrderDetail;

import com.leyou.order.pojo.OrderStatus;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

import java.util.*;

import java.util.stream.Collectors;

@Service

@Slf4j

public class OrderService {

@Autowired

private OrderMapper orderMapper;

@Autowired

private OrderDetailMapper detailMapper;

@Autowired

private OrderStatusMapper statusMapper;

@Autowired

private IdWorker idWorker;

@Autowired

private GoodsClient goodsClient;

@Transactional

public Long createOrder(OrderDTO orderDTO) {

//新增订单

Order order = new Order();

//订单编号

long orderId = idWorker.nextId();

order.setOrderId(orderId);

order.setCreateTime(new Date());

order.setPaymentType(orderDTO.getPaymentType());

//用户信息

UserInfo user = UserInterceptor.getLoginUser();

order.setUserId(user.getId());

order.setBuyerNick(user.getUserName());

order.setBuyerRate(false);

//收货人地址信息

//获取收货人信息

AddressDTO addr = AddressClient.findById(orderDTO.getAddressId());

order.setReceiver(addr.getName());

order.setReceiverAddress(addr.getAddress());

order.setReceiverCity(addr.getCity());

order.setReceiverDistrict(addr.getDistrict());

order.setReceiverMobile(addr.getPhone());

order.setReceiverState(addr.getState());

order.setReceiverZip(addr.getZipCode());

//金额相关

//把CartDTO 转为一个map,key是sku的id,值是num

Map<Long, Integer> numMap = orderDTO.getCarts().stream()

.collect(Collectors.toMap(CartDTO::getSkuId, CartDTO::getNum));

//获取所有的sku的id

Set ids = numMap.keySet();

//根据id查询sku

List skus = goodsClient.querySkuBySpuId(new ArrayList<>(ids));

//准备orderDetail

List details = new ArrayList<>();

long totalPay = 0L;

for (Sku sku : skus) {

//计算商品总价

totalPay += sku.getPrice() * numMap.get(sku.getId());

//封装OrderDetail

OrderDetail detail = new OrderDetail();

detail.setImage(StringUtils.substringBefore(sku.getImages(),“,”));

detail.setNum(numMap.get(sku.getId()));

detail.setOrderId(orderId);

detail.setOwnSpec(sku.getOwnSpec());

detail.setPrice(sku.getPrice());

detail.setSkuId(sku.getId());

detail.setTitle(sku.getTitle());

details.add(detail);

}

//总金额

order.setTotalPay(totalPay);

//实付金额:总金额+邮费-优惠金额

order.setActualPay(totalPay + order.getPostFee() - 0);

//把order写入数据库

int count = orderMapper.insertSelective(order);

if(count != 1){

log.error(“[创建订单] 创建订单失败,orderId:{}”,orderId);

throw new LyException(ExceptionEnum.CREATE_ORDER_ERROR);

}

//新增订单详情

count = detailMapper.insertList(details);

if(count != details.size()){

log.error(“[创建订单状态] 创建订单失败,orderId:{}”,orderId);

throw new LyException(ExceptionEnum.CREATE_ORDER_ERROR);

}

//新增订单状态

OrderStatus orderStatus = new OrderStatus();

orderStatus.setCreateTime(order.getCreateTime());

orderStatus.setOrderId(orderId);

orderStatus.setStatus(OrderStatusEnum.UN_PAY.value());

count = statusMapper.insertSelective(orderStatus);

if (count != 1) {

log.error(“[创建订单状态] 创建订单失败,orderId:{}”,orderId);

throw new LyException(ExceptionEnum.CREATE_ORDER_ERROR);

}

//减库存

List carts = orderDTO.getCarts();

goodsClient.decreaseStock(carts);

return orderId;

}

}

2.0.7 测试创建订单

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.订单

===============================================================

(1)点击支付后会跳转到支付页面


在这里插入图片描述

(2)实现上述所对应的接口(先实现查询订单)


(a)OrderController当中queryOrderById方法

在这里插入图片描述

@GetMapping(“{id}”)

public ResponseEntity queryOrderById(@PathVariable(“id”) Long id){

return ResponseEntity.ok(orderService.queryOrderById(id));

}

(b)完善OrderService当中对应的方法

在这里插入图片描述

在这里插入图片描述

ORDER_NOT_FOUND(404,“订单不存在”),

ORDER_DETAIL_NOT_FOUND(404,“订单详情不存在”),

ORDER_STATUS_NOT_FOUND(404,“订单状态不存在”),

在这里插入图片描述

public Order queryOrderById(Long id) {

//查询订单

Order order = orderMapper.selectByPrimaryKey(id);

if(order == null){

//订单不存在

throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);

}

//查询订单详情

OrderDetail detail = new OrderDetail();

detail.setOrderId(id);

List details = detailMapper.select(detail);

if(CollectionUtils.isEmpty(details)){

throw new LyException(ExceptionEnum.ORDER_DETAIL_NOT_FOUND);

}

order.setOrderDetails(details);

//查询订单状态

OrderStatus orderStatus = statusMapper.selectByPrimaryKey(id);

if(orderStatus == null){

throw new LyException(ExceptionEnum.ORDER_STATUS_NOT_FOUND);

}

order.setOrderStatus(orderStatus);

return order;

}

重新运行并测试

在这里插入图片描述

刷新支付页面

成功显示对应的数据

在这里插入图片描述

3.微信支付

=================================================================

(1)官网:


https://pay.weixin.qq.com/index.php

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

填写信息

在这里插入图片描述

(2)开发流程(实现后台代码)


模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。

商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url;

商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。

注意:code_url有效期为2小时,过期后扫码不能再发起支付。

流程图:

在这里插入图片描述

  • 1、商户生成订单

  • 2、商户调用微信下单接口,获取预交易的链接

  • 3、商户将链接生成二维码图片,展示给用户;(码中包含,价格,收款方,订单号)

  • 4、用户支付并确认

  • 5、支付结果通知:

  • 微信异步通知商户支付结果,商户告知微信支付接收情况

  • 商户如果没有收到通知,可以调用接口,查询支付状态

  • 6、如果支付成功,发货,修改订单状态

在前面的业务中,我们已经完成了:

  • 1、生成订单

接下来,我们需要做的是:

  • 2、调用微信接口,生成链接。

  • 3、并且生成二维码图片

1)创建PayConfig

在这里插入图片描述

package com.leyou.order.config;

import com.github.wxpay.sdk.WXPayConfig;

import lombok.Data;

import java.io.InputStream;

@Data

public class PayConfig implements WXPayConfig {

private String appID; //公众账号ID

private String mchID; //商户号

private String key; //生成签名的密钥

private int httpConnectTimeoutMs; //连接超时时间

private int httpReadTimeoutMs; //读取超时时间

private String notifyUrl;// 下单通知回调地址

@Override

public InputStream getCertStream() {

return null;

}

}

2)创建上述对应的配置文件

在这里插入图片描述

pay:

appId: wx8397f8696b538317

mchId: 1473426802

key: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb

connectTimeoutMs: 5000

readTimeoutMs: 10000

notifyUrl: http://k4v7yt.natappfree.cc/notify/wxpay

3)自定义WXPayConfiguration类封装PayConfig

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.config;

import com.github.wxpay.sdk.WXPay;

import com.github.wxpay.sdk.WXPayConstants;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import javax.persistence.Basic;

@Configuration

public class WXPayConfiguration {

@Bean

@ConfigurationProperties(prefix = “ly.pay”)

public PayConfig payConfig(){

return new PayConfig();

}

@Bean

public WXPay wxPay(PayConfig payConfig){

return new WXPay(payConfig, WXPayConstants.SignType.HMACSHA256);

}

}

4)支付工具类PayHelper

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.utils;

import com.github.wxpay.sdk.WXPay;

import com.github.wxpay.sdk.WXPayConstants;

import com.leyou.common.enums.ExceptionEnum;

import com.leyou.common.exception.LyException;

import com.leyou.order.config.PayConfig;

import com.leyou.order.config.WXPayConfiguration;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import java.util.HashMap;

import java.util.Map;

@Component //将其添加到Spring当中

@Slf4j

public class PayHelper {

@Autowired

private WXPay wxPay;

@Autowired

private PayConfig config;

public String createPayUrl(Long orderId, Long totalPay, String description) {

try {

HashMap<String, String> data = new HashMap<>();

//描述

data.put(“body”, description);

//订单号

data.put(“out_trade_no”, orderId.toString());

//货币(默认就是人民币)

//data.put(“fee_type”, “CNY”);

//总金额

data.put(“total_fee”, totalPay.toString());

//调用微信支付的终端ip

data.put(“spbill_create_ip”, “127.0.0.1”);

//回调地址

data.put(“notify_url”, config.getNotifyUrl());

//交易类型为扫码支付

data.put(“trade_type”, “NATIVE”);

//利用wxPay工具,完成下单

Map<String, String> result = this.wxPay.unifiedOrder(data);

//判断通信的标识

String return_code = result.get(“return_code”);

if(WXPayConstants.FAIL.equals(return_code)){

//通信失败

log.error(“[微信下单] 微信下单通信失败,失败原因:{}”,result.get(“return_msg”));

throw new LyException(ExceptionEnum.WX_PAY_ORDER_FAIL);

}

//判断业务标识

String result_code = result.get(“result_code”);

if(WXPayConstants.FAIL.equals(result_code)){

//通信失败

log.error(“[微信下单] 微信下单业务失败,错误码:{},错误原因:{}”,result.get(“err_code”),result.get(“err_code_des”));

throw new LyException(ExceptionEnum.WX_PAY_ORDER_FAIL);

}

//下单成功,获取支付连接

String url = result.get(“code_url”);

return url;

} catch (Exception e) {

log.error(“【微信下单】创建预交易订单异常”, e);

return null;

}

}

}

5)编写OrderController的createOrderUrl方法

在这里插入图片描述

/*

创建支付链接

*/

@GetMapping(“/url/{id}”)

public ResponseEntity createPayUrl(@PathVariable(“id”) Long orderId ){

return ResponseEntity.ok(orderService.createPayUrl(orderId));

}

6)完善OrderService

在这里插入图片描述

ORDER_STATUS_ERROR(400,“订单状态异常”),

在这里插入图片描述

在这里插入图片描述

@Autowired

private PayHelper payHelper;

在这里插入图片描述

public String createPayUrl(Long orderId) {

//查询订单 //获取订单总金额

Order order = queryOrderById(orderId);

//判断订单状态

if(order.getOrderStatus().getStatus() != OrderStatusEnum.UN_PAY.value()){

//订单状态异常

throw new LyException(ExceptionEnum.ORDER_STATUS_ERROR);

}

//获取总金额

Long actualPay = order.getActualPay();

actualPay = 1L;

//获取商品描述

OrderDetail orderDetail = order.getOrderDetails().get(0);

String desc = orderDetail.getTitle();

return payHelper.createPayUrl(orderId,actualPay,desc);

}

查询启动运行并测试

在这里插入图片描述

刷新支付页面

在这里插入图片描述

返回支付链接

在这里插入图片描述

(3)生成二维码


1)二维码生成插件qrious

qrious是一款基于HTML5 Canvas的纯JS二维码生成插件。

通过qrious.js可以快速生成各种二维码,你可以控制二维码的尺寸颜色,

还可以将生成的二维码进行Base64编码。

官网

https://github.com/neocotic/qrious

在js目录下引入qrcode.min.js

在这里插入图片描述

有pay.html当中已经写好了代码

刷新页面

在这里插入图片描述

(4)修改订单状态


1)内网穿透(外网访问本地服务)

内网穿透,也即 NAT 穿透,进行 NAT 穿透是为了使具有某一个特定源 IP 地址和源端口号的数据包不被 NAT 设备屏蔽而正确路由到内网主机。

下面就相互通信的主机在网络中与 NAT 设备的相对位置介绍内网穿透方法。

UDP 内网穿透的实质是利用路由器上的NAT 系统。

NAT 是一种将私有(保留)地址转化为合法IP地址的转换技术,它被广泛应用于各种类型 Internet 接入方式和各种类型的网络中。

NAT可以完成重用地址,并且对于内部的网络结构可以实现对外隐蔽。

免费的内网穿透工具

NATAPP

官网:https://natapp.cn/

在这里插入图片描述

登录注册

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

点击复制,即可得到 authtoken 这个authtoken便是您的隧道登录凭证.如这里得到的authtoken为9ab6b9040a624f40

下载客户端

在这里插入图片描述

在这里插入图片描述

解压

在这里插入图片描述

8.运行natapp

natapp支持两种运行方式

a) config.ini方式 (推荐)

根据操作系统下载不同的config.ini文件到刚才下载的natapp.exe同级目录 详见

将第7步得到的authtoken填进去 (其他地方都不填),然后保存

#将本文件放置于natapp同级目录程序将读取[default]段

#在命令行参数模式如natapp -authtoken=xxx等厢同参数将会覆盖掉此配置

[default]

authtoken=9ab6b9040a624f40

#对应一条隧道的authtoken

c1ienttoken=

#对应客户端的c1ienttoken,将会忽略authtoken,若无请留空,

1og=none

#1og且志文件,可以是none 代表不记录或者stdout.代表直接屏幕输出﹐默认为none

1oglevel=INFO

#日志等级DEBUG,INFO,WARNING,ERROR默认为 DEBUG

http_proxy=

#代理设置如http://110.123.10.10:3128

windows下,直接双击natapp.exe 即可.

在Linux/Mac 下 需要先给执行权限

chmod a+x natapp

然后再运行

./natapp

b) cmd -authtoken= 参数方式运行.

windows ,点击开始->运行->命令行提示符 后进入 natapp.exe的目录

运行

natapp -authtoken=9ab6b9040a624f40

linux ,同样给予可执行权限之后,运行

./natapp -authtoken=9ab6b9040a624f40

注意参数输入正确性,不要有多余的空格等!

9.运行成功,都可以得到如下界面:

在这里插入图片描述

Tunnel Status Online 代表链接成功

Version 当前客户端版本,如果有新版本,会有提示

Forwarding 当前穿透 网址 或者端口

Web Interface 是本地Web管理界面,可在隧道配置打开或关闭,仅用于web开发测试

Total Connections 总连接数

Avg Conn Time 0.00ms 这里不代表,不代表,不代表 延时,需要注意!

10.将natapp分配的网址(上图Forwarding ),鼠标选定然后复制下来(选定之后单击鼠标右键),在浏览器中访问,可以看到内网穿透成功了!

在这里插入图片描述

该网址 就是可以全球访问的网址,可以发给您的小伙伴试试 😃

2)在ly-order当中重新定义一个controller

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

下载客户端

在这里插入图片描述

在这里插入图片描述

解压

在这里插入图片描述

8.运行natapp

natapp支持两种运行方式

a) config.ini方式 (推荐)

根据操作系统下载不同的config.ini文件到刚才下载的natapp.exe同级目录 详见

将第7步得到的authtoken填进去 (其他地方都不填),然后保存

#将本文件放置于natapp同级目录程序将读取[default]段

#在命令行参数模式如natapp -authtoken=xxx等厢同参数将会覆盖掉此配置

[default]

authtoken=9ab6b9040a624f40

#对应一条隧道的authtoken

c1ienttoken=

#对应客户端的c1ienttoken,将会忽略authtoken,若无请留空,

1og=none

#1og且志文件,可以是none 代表不记录或者stdout.代表直接屏幕输出﹐默认为none

1oglevel=INFO

#日志等级DEBUG,INFO,WARNING,ERROR默认为 DEBUG

http_proxy=

#代理设置如http://110.123.10.10:3128

windows下,直接双击natapp.exe 即可.

在Linux/Mac 下 需要先给执行权限

chmod a+x natapp

然后再运行

./natapp

b) cmd -authtoken= 参数方式运行.

windows ,点击开始->运行->命令行提示符 后进入 natapp.exe的目录

运行

natapp -authtoken=9ab6b9040a624f40

linux ,同样给予可执行权限之后,运行

./natapp -authtoken=9ab6b9040a624f40

注意参数输入正确性,不要有多余的空格等!

9.运行成功,都可以得到如下界面:

在这里插入图片描述

Tunnel Status Online 代表链接成功

Version 当前客户端版本,如果有新版本,会有提示

Forwarding 当前穿透 网址 或者端口

Web Interface 是本地Web管理界面,可在隧道配置打开或关闭,仅用于web开发测试

Total Connections 总连接数

Avg Conn Time 0.00ms 这里不代表,不代表,不代表 延时,需要注意!

10.将natapp分配的网址(上图Forwarding ),鼠标选定然后复制下来(选定之后单击鼠标右键),在浏览器中访问,可以看到内网穿透成功了!

在这里插入图片描述

该网址 就是可以全球访问的网址,可以发给您的小伙伴试试 😃

2)在ly-order当中重新定义一个controller

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-uiSUxxLw-1714921708401)]

[外链图片转存中…(img-DdGHJYX4-1714921708401)]

[外链图片转存中…(img-h2JV06la-1714921708401)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值