当用户点击支付宝支付
时,在添加选课记录后,请求订单微服务下单,生成支付二维码。
接口信息
参数DTO
AddOrderDTO
@Data @ToString public class AddOrderDTO {
@NotNull(message = "价格不能为空") private Float totalPrice;
@NotEmpty(message = "未知订单类型") private String orderType;
private String orderName;
@NotEmpty(message = "请添加订单描述") private String orderDescrip;
@NotEmpty(message = "订单明细不可为空") private String orderDetail;
@NotEmpty(message = "请先选课") private String outBusinessId;
}
|
视图VO
PayRecordVO
@Data @ToString @EqualsAndHashCode(callSuper = true) public class PayRecordVO extends XcPayRecord {
private String qrcode;
}
|
流程分析
用户点击支付宝支付,请求二维码接口,生成订单记录,商品信息,和支付记录;携带支付记录中的支付编号返回给前端;用户使用支付宝APP扫码时,会请求二维码中的地址,其中就有支付编号。
二维码信息如下,该地址为扫码下单接口地址,即本项目的地址。由支付宝发起请求
二维码工具
在learning-online-base
工程的pom文件中添加二维码生成的依赖:
pom.xml <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> </dependency>
|
二维码生成工具
QRCodeUtil
public class QRCodeUtil { private final Logger log = LoggerFactory.getLogger(QRCodeUtil.class);
public String createQRCode(String content, int width, int height) throws IOException { String resultImage = ""; if (!StringUtils.isEmpty(content)) { ServletOutputStream stream = null; ByteArrayOutputStream os = new ByteArrayOutputStream(); @SuppressWarnings("rawtypes") HashMap<EncodeHintType, Comparable> hints = new HashMap<>(); hints.put(EncodeHintType.CHARACTER_SET, "utf-8"); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); hints.put(EncodeHintType.MARGIN, 1);
try { QRCodeWriter writer = new QRCodeWriter(); BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, width, height, hints); BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix); ImageIO.write(bufferedImage, "png", os); resultImage = "data:image/png;base64," + EncryptUtil.encodeBase64(os.toByteArray()); return resultImage; } catch (Exception e) { log.error("生成二维码出错", e); throw new RuntimeException("生成二维码出错"); } finally { if (stream != null) { stream.flush(); stream.close(); } } } return null; } }
|
编码工具
EncryptUtilpublic class EncryptUtil { private static final Logger logger = LoggerFactory.getLogger(EncryptUtil.class);
public static String encodeBase64(byte[] bytes){ String encoded = Base64.getEncoder().encodeToString(bytes); return encoded; }
public static byte[] decodeBase64(String str){ byte[] bytes = null; bytes = Base64.getDecoder().decode(str); return bytes; }
public static String encodeUTF8StringBase64(String str){ String encoded = null; try { encoded = Base64.getEncoder().encodeToString(str.getBytes("utf-8")); } catch (UnsupportedEncodingException e) { logger.warn("不支持的编码格式",e); } return encoded;
}
public static String decodeUTF8StringBase64(String str){ String decoded = null; byte[] bytes = Base64.getDecoder().decode(str); try { decoded = new String(bytes,"utf-8"); }catch(UnsupportedEncodingException e){ logger.warn("不支持的编码格式",e); } return decoded; }
public static String encodeURL(String url) { String encoded = null; try { encoded = URLEncoder.encode(url, "utf-8"); } catch (UnsupportedEncodingException e) { logger.warn("URLEncode失败", e); } return encoded; }
public static String decodeURL(String url) { String decoded = null; try { decoded = URLDecoder.decode(url, "utf-8"); } catch (UnsupportedEncodingException e) { logger.warn("URLDecode失败", e); } return decoded; }
public static void main(String [] args){ String str = "abcd{'a':'b'}"; String encoded = EncryptUtil.encodeUTF8StringBase64(str); String decoded = EncryptUtil.decodeUTF8StringBase64(encoded); System.out.println(str); System.out.println(encoded); System.out.println(decoded);
String url = "== wo"; String urlEncoded = EncryptUtil.encodeURL(url); String urlDecoded = EncryptUtil.decodeURL(urlEncoded); System.out.println(url); System.out.println(urlEncoded); System.out.println(urlDecoded); } }
|
创建订单Service
在com.swx.orders.service
包下的 OrderService 中定义创建订单方法:
XcOrdersService
public interface OrderService {
public PayRecordVO createOrder(String userId, AddOrderDTO dto); }
|
在实现类中实现该方法
OrderServiceImpl@Slf4j @Service public class OrderServiceImpl implements OrderService {
private final XcOrdersService xcOrdersService; private final XcPayRecordService xcPayRecordService;
@Value("${pay.qrcodeUrl}") private String qrcodeUrl;
public OrderServiceImpl(XcOrdersService xcOrdersService, XcPayRecordService xcPayRecordService) { this.xcOrdersService = xcOrdersService; this.xcPayRecordService = xcPayRecordService; }
@Override @Transactional(rollbackFor = Exception.class) public PayRecordVO createOrder(String userId, AddOrderDTO dto) { XcOrders orders = xcOrdersService.saveXcOrders(userId, dto);
if (orders.getStatus().equals("601002")) { throw new BizException("订单已支付"); } XcPayRecord payRecord = xcPayRecordService.createPayRecord(orders);
String url = String.format(qrcodeUrl, payRecord.getPayNo()); QRCodeUtil qrCodeUtil = new QRCodeUtil(); String qrCode = null; try { qrCode = qrCodeUtil.createQRCode(url, 200, 200); } catch (IOException e) { throw new BizException("生成二维码出错"); }
PayRecordVO payRecordVO = new PayRecordVO(); BeanUtils.copyProperties(payRecord, payRecordVO); payRecordVO.setQrcode(qrCode); return payRecordVO; } }
|
新增订单Service
首先根据用户ID和选课ID查询订单信息,确保一个用户只能对一个选课创建一个订单,然后根据参数组装 XcOrders 实体类,使用雪花算法生成订单号,并将数据保存到数据库中。
在com.swx.orders.service
包下的 XcOrdersService 中定义新增订单方法:
XcOrdersServicepublic interface XcOrdersService extends IService<XcOrders> {
XcOrders saveXcOrders(String userId, AddOrderDTO dto); }
|
在实现类中实现该方法
XcOrdersServiceImpl@Service public class XcOrdersServiceImpl extends ServiceImpl<XcOrdersMapper, XcOrders> implements XcOrdersService {
private final XcOrdersGoodsService xcOrdersGoodsService;
public XcOrdersServiceImpl(XcOrdersGoodsService xcOrdersGoodsService) { this.xcOrdersGoodsService = xcOrdersGoodsService; }
@Override public XcOrders saveXcOrders(String userId, AddOrderDTO dto) { XcOrders order = getOneByBizId(userId, dto.getOutBusinessId()); if (order != null) { return order; }
order = new XcOrders(); BeanUtils.copyProperties(dto, order); order.setId(IdWorkerUtil.getInstance().nextId()); order.setCreateDate(LocalDateTime.now()); order.setStatus("600001"); order.setUserId(userId); order.setOrderType("60201"); boolean save = save(order); if (!save) { throw new BizException("添加订单失败"); }
Long orderId = order.getId(); String orderDetailJson = dto.getOrderDetail(); List<XcOrdersGoods> xcOrdersGoods = JSON.parseArray(orderDetailJson, XcOrdersGoods.class); for (XcOrdersGoods xcOrdersGood : xcOrdersGoods) { xcOrdersGood.setOrderId(orderId); } boolean saveGoods = xcOrdersGoodsService.saveBatch(xcOrdersGoods); return order; }
public XcOrders getOneByBizId(String userId, String bizId) { return getOne(Wrappers.<XcOrders>lambdaQuery().eq(XcOrders::getUserId, userId).eq(XcOrders::getOutBusinessId, bizId)); } }
|
新增支付记录Service
创建支付记录,此时的支付状态为:未支付。
在com.swx.orders.service
包下的 XcPayRecordService 中定义新增订单方法:
XcPayRecordServicepublic interface XcPayRecordService extends IService<XcPayRecord> {
XcPayRecord createPayRecord(XcOrders orders); }
|
在实现类中实现该方法
XcPayRecordServiceImpl
@Service public class XcPayRecordServiceImpl extends ServiceImpl<XcPayRecordMapper, XcPayRecord> implements XcPayRecordService {
@Override public XcPayRecord createPayRecord(XcOrders orders) { XcPayRecord xcPayRecord = new XcPayRecord(); xcPayRecord.setPayNo(IdWorkerUtil.getInstance().nextId()); xcPayRecord.setOrderId(orders.getId()); xcPayRecord.setOrderName(orders.getOrderName()); xcPayRecord.setTotalPrice(orders.getTotalPrice()); xcPayRecord.setCurrency("CNY"); xcPayRecord.setCreateDate(LocalDateTime.now()); xcPayRecord.setStatus("601001"); xcPayRecord.setUserId(orders.getUserId()); boolean save = save(xcPayRecord); if (!save) { throw new BizException("添加支付记录失败"); } return xcPayRecord; } }
|
创建订单Controller
@Api(value = "订单支付接口", tags = "订单支付接口") @RestController public class OrderController { private final OrderService orderService;
public OrderController(OrderService orderService) { this.orderService = orderService; }
@ApiOperation("生成支付二维码") @PostMapping("/generatepaycode") public PayRecordVO generatePayCode(@RequestBody AddOrderDTO dto) { SecurityUtil.XcUser user = SecurityUtil.getUser(); String userId = user.getId(); return orderService.createOrder(userId, dto); } }
|