}
package com.leyou.order.config;
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(“/**”);
}
}
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=‘订单状态表’;
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;// 评价时间
}
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;
}
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;
}
}
package com.leyou.order.service;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
}
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 {
}
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;
}
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碰撞(由datacenter
和workerld
作区分),
并且效率较高。
经测试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 测试创建订单
===============================================================
(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;
}
重新运行并测试
刷新支付页面
成功显示对应的数据
=================================================================
https://pay.weixin.qq.com/index.php
填写信息
模式二与模式一相比,流程更为简单,不依赖设置的回调支付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);
}
查询启动运行并测试
刷新支付页面
返回支付链接
1)二维码生成插件qrious
qrious是一款基于HTML5 Canvas的纯JS二维码生成插件。
通过qrious.js可以快速生成各种二维码,你可以控制二维码的尺寸颜色,
还可以将生成的二维码进行Base64编码。
官网
https://github.com/neocotic/qrious
在js目录下引入qrcode.min.js
Java面试核心知识点笔记
其中囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。
Java中高级面试高频考点整理
最后分享Java进阶学习及面试必备的视频教学
//img-blog.csdnimg.cn/aead30a115f14dfbb62ee1a336f80e25.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd55uS5a2QaXRibHVlYm94,size_20,color_FFFFFF,t_70,g_se,x_16)
@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);
}
查询启动运行并测试
刷新支付页面
返回支付链接
1)二维码生成插件qrious
qrious是一款基于HTML5 Canvas的纯JS二维码生成插件。
通过qrious.js可以快速生成各种二维码,你可以控制二维码的尺寸颜色,
还可以将生成的二维码进行Base64编码。
官网
https://github.com/neocotic/qrious
在js目录下引入qrcode.min.js
Java面试核心知识点笔记
其中囊括了JVM、锁、并发、Java反射、Spring原理、微服务、Zookeeper、数据库、数据结构等大量知识点。
[外链图片转存中…(img-vECC6YLV-1714388948346)]
Java中高级面试高频考点整理
[外链图片转存中…(img-BYxHokRe-1714388948347)]
[外链图片转存中…(img-aGtkarYV-1714388948347)]
最后分享Java进阶学习及面试必备的视频教学
[外链图片转存中…(img-TV36iLr2-1714388948347)]