文档地址:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_1
封装的工具类
package com.qf.fmall.utils;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.http.HttpRequest;
import org.apache.shiro.crypto.hash.Md5Hash;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;
public class WeixinPayUtils {
public static final String orderSubmit="https://api.mch.weixin.qq.com/pay/unifiedorder";
public static final String orderQueryUrl="https://api.mch.weixin.qq.com/pay/orderquery";
public static final String appkey="sbNCm1JnevqI36LrEaxFwcaT0hkGxFnC";
public static void main(String[] args) {
String s = weixinPay("sadsadasa", "wqeqweqeq");
System.out.println(s);
}
public static String weixinPay(String oid,String describe){
//1. 组织请求map,必传字段如下
//这里可有序可以无序,但是计算签名的时候必须有序,规定!按Key排序
TreeMap<String, String> treeMap = new TreeMap<>();
treeMap.put("appid","wx632c8f211f8122c6");//商户的应用ID
treeMap.put("mch_id","1497984412"); //商户号
treeMap.put("notify_url","http://47.118.45.73:8080/pay/callback"); // 商户提供的回调地址
String randomstr = UUID.randomUUID().toString().replaceAll("-", ""); //生成随机数
treeMap.put("nonce_str",randomstr); // 长度32个字符的随机字符串,干扰项
treeMap.put("body",describe); // 订单描述信息
treeMap.put("out_trade_no",oid); // 订单编号
treeMap.put("total_fee","1"); // 1 分钱
treeMap.put("trade_type","NATIVE"); // 支付类型
treeMap.put("fee_type","CNY"); // 币种
treeMap.put("sign_type","MD5"); // 签名算法
//计算签名
String tempString = treeMap.toString().replace("{", "").replace("}", "").replace(", ","&")+"&key="+appkey;
String sign = new Md5Hash(tempString).toHex().toUpperCase();
treeMap.put("sign",sign);
//map转XML字符串
String xml = XmlUtil.mapToXmlStr(treeMap);
//用Hutool工具类发送post请求
String result = HttpRequest.post(orderSubmit).body(xml).execute().body();
// xml str ----> map
Map<String, Object> resultMap = XmlUtil.xmlToMap(result);
String url = (String) resultMap.get("code_url");
return url;
}
public static String queryStatus(String orderId) {
//1. 组织请求map,必传字段如下
//这里可有序可以无序,但是计算签名的时候必须有序,规定!按Key排序
TreeMap<String, String> treeMap = new TreeMap<>();
treeMap.put("appid","wx632c8f211f8122c6");//商户的应用ID
treeMap.put("mch_id","1497984412"); //商户号
String randomstr = UUID.randomUUID().toString().replaceAll("-", ""); //生成随机数
treeMap.put("nonce_str",randomstr); // 长度32个字符的随机字符串,干扰项
treeMap.put("out_trade_no",orderId); // 订单编号
treeMap.put("sign_type","MD5"); // 签名算法
//计算签名
String tempString = treeMap.toString().replace("{", "").replace("}", "").replace(", ","&")+"&key="+appkey;
String sign = new Md5Hash(tempString).toHex().toUpperCase();
treeMap.put("sign",sign);
//map转XML字符串
String xml = XmlUtil.mapToXmlStr(treeMap);
//用Hutool工具类发送post请求
String result = HttpRequest.post(orderQueryUrl).body(xml).execute().body();
// xml str ----> map
Map<String, Object> resultMap = XmlUtil.xmlToMap(result);
String trade_state = (String) resultMap.get("trade_state");
return trade_state;
}
}
controller
package com.qf.fmall.controller;
import com.qf.fmall.common.ResultVo;
import com.qf.fmall.entity.Orders;
import com.qf.fmall.service.IOrdersService;
import com.qf.fmall.utils.FmallConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.HashMap;
/**
* <p>
* 订单 前端控制器
* </p>
*
* @author jmj
* @since 2023-08-24
*/
@RestController
@RequestMapping("/order")
@CrossOrigin
@Slf4j
public class OrdersController {
@Autowired
private IOrdersService ordersService;
@PostMapping("/add")
public ResultVo add(Integer[] cids,@RequestBody Orders orders) throws Exception {
log.info("入参为:cid={},orders={}", Arrays.toString(cids),orders);
HashMap<String,Object> map= ordersService.addOrder(cids,orders);
return ResultVo.vo(FmallConstants.SUCCESS_CODE,FmallConstants.SUCCESS_MSG,map);
}
@GetMapping("/status/{orderId}")
public ResultVo status(@PathVariable("orderId") String orderId){
log.info("入参为:orderId={}",orderId);
String s= ordersService.status(orderId);
log.info("传回来的状态:{}",s);
return ResultVo.vo(FmallConstants.SUCCESS_CODE,FmallConstants.SUCCESS_MSG,s);
}
}
service
package com.qf.fmall.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qf.fmall.entity.*;
import com.qf.fmall.mapper.OrdersMapper;
import com.qf.fmall.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qf.fmall.utils.WeixinPayUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* <p>
* 订单 服务实现类
* </p>
*
* @author jmj
* @since 2023-08-24
*/
@Service
@Slf4j
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersService {
@Autowired
private IShoppingCartService shoppingCartService;
@Autowired
private IProductSkuService iProductSkuService;
@Autowired
private IProductService iProductService;
@Autowired
private IOrderItemService iOrderItemService;
@Transactional(rollbackFor = Exception.class)
@Override
public synchronized HashMap<String, Object> addOrder(Integer[] cids, Orders orders) throws Exception {
//1. 查询用户购物车中的对应的商品套餐库存量是否还足够
List<Map<String, Object>> shoppingcars = shoppingCartService.listMaps(new QueryWrapper<ShoppingCart>().select("sku_id skuId, SUM(`cart_num`) `sum`").in("cart_id", cids).groupBy("sku_id"));
log.info("查出来的购物车按ID分类计算的集合为{}",shoppingcars);
TreeSet<String> productIds = new TreeSet<>();//查出购物车中所有的productId
for (Map<String, Object> shoppingcar : shoppingcars) {
ProductSku one = iProductSkuService.getOne(new QueryWrapper<ProductSku>().eq("sku_id", shoppingcar.get("skuId")));
productIds.add(one.getProductId());
Integer stock = one.getStock();
Double cartNum = (Double) shoppingcar.get("sum");
if (cartNum>stock){
throw new Exception("库存不足,购买失败");
}
// 减少库存
one.setStock((stock-cartNum.intValue()));
iProductSkuService.updateById(one);
}
//过了这个循环没有抛异常,就可以往下走,说明库存够
//2. 生成唯一的订单编号
String orderId = UUID.randomUUID().toString().replace("-", "");
orders.setOrderId(orderId);
//3. 生成订单主表(orders表)中的数据
orders.setCreateTime(new Date());//创建时间
orders.setUpdateTime(new Date());//修改时间
// 产品名称:untitled用逗号隔开
//先查Product产品ID
StringBuilder builder = new StringBuilder();
for (String productId : productIds) {
//查出每个产品中的名字,放到字符串缓冲流中,并用逗号拼接
builder.append(iProductService.getOne(new QueryWrapper<Product>().eq("product_id",productId)).getProductName());
builder.append(",");
}
//删除最后一个逗号,拼接转化为字符串
String untitled = builder.deleteCharAt(builder.length() - 1).toString();
orders.setUntitled(untitled);
// 订单的状态,初始值1,待付款 status
orders.setStatus("1");//待付款
// 逻辑删除状态,初始值为0 delete_status
orders.setDeleteStatus(0);
log.info("生成的order对象为:{}",orders);
//将对象添加到订单表里
this.save(orders);
//4. 生成订单明细表的数据 (OrderItem表)
//有几个cid就有几个定单
ArrayList<OrderItem> orderItems = new ArrayList<>();
for (Integer cartId : cids) {
OrderItem orderItem = new OrderItem();
String orderItemId = UUID.randomUUID().toString().replace("-", "");
//设置32位随机数作为ItemId 放入orderItem对象里
orderItem.setItemId(orderItemId);
//订单Id注入
orderItem.setOrderId(orders.getOrderId());
//查一下购物车 id
ShoppingCart shoppingCart = shoppingCartService.getOne(new QueryWrapper<ShoppingCart>().eq("cart_id", cartId));
ProductSku productSku = iProductSkuService.getOne(new QueryWrapper<ProductSku>().eq("sku_id", shoppingCart.getSkuId()));
Product product = iProductService.getOne(new QueryWrapper<Product>().eq("product_id", productSku.getProductId()));
//查一下产品对象
//注入产品ID
orderItem.setProductId(productSku.getProductId());
orderItem.setProductName(product.getProductName());
orderItem.setProductImg(productSku.getSkuImg());
orderItem.setSkuId(productSku.getSkuId());
orderItem.setSkuName(productSku.getSkuName());
orderItem.setProductPrice(new BigDecimal(productSku.getSellPrice()));
orderItem.setBuyCounts(Integer.parseInt(shoppingCart.getCartNum()));
orderItem.setTotalAmount(new BigDecimal(productSku.getSellPrice()*Integer.parseInt(shoppingCart.getCartNum())));
//转换时间
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
orderItem.setBasketDate(format.parse(shoppingCart.getCartTime()));
orderItem.setIsComment(0);
log.info("每个订单详情对象:{}",orderItem);
//保存到集合
orderItems.add(orderItem);
}
//5. 保存订单到数据库
log.info("添加到数据库的订单集合对象:{}",orderItems);
iOrderItemService.saveBatch(orderItems);
//清空购物车
List<Integer> list = Arrays.asList(cids);
shoppingCartService.removeBatchByIds(list);
//6. 跟微信支付平台交互,获取可以支付的url
String url = WeixinPayUtils.weixinPay(orders.getOrderId(),orders.getUntitled());
log.info("url:{}",url);
//7. 组织返回的map数据
HashMap<String, Object> map = new HashMap<>();
map.put("orderId",orders.getOrderId());
map.put("productNames",orders.getUntitled());
map.put("payUrl",url);
return map;
}
@Override
public String status(String orderId) {
//1.掉微信支付的查询订单状态接口,获取订单状态
String status= WeixinPayUtils.queryStatus(orderId);
//2.如果支付成功了,需要修改订单表中的订单状态和支付时间
if (status.equals("SUCCESS")){
Orders orders = new Orders();
orders.setOrderId(orderId);
orders.setStatus("2");
updateById(orders);
return "2";
}
return "-1";
}
}
封装工具类
pom
<!-- 微信支付 sdk (sdk用来简化调用的工具包) -->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
</dependencies>
WxSdkUtils
package com.qf.fmall2302.wxpay;
import com.github.wxpay.sdk.WXPay;
import com.github.wxpay.sdk.WXPayConfig;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class WxSdkUtils {
public String getPayUrl(String orderid,String desc,Long amount) throws Exception {
// 创建sdk的核心对象
WXPay wxPay = new WXPay(new MyWxConfig());
HashMap<String, String> map = new HashMap<>();
String randomstr = UUID.randomUUID().toString().replaceAll("-", "");
map.put("nonce_str",randomstr); // 长度32个字符的随机字符串,干扰项
map.put("body",desc); // 订单描述信息
map.put("out_trade_no",orderid); // 订单编号
map.put("total_fee","1"); // 1 分钱
map.put("trade_type","NATIVE"); // 支付类型
map.put("fee_type","CNY"); // 币种
map.put("notify_url","http://47.118.45.73:8080/pay/callback"); // 商户提供的回调地址
map.put("sign_type","MD5"); // 签名算法
Map<String, String> result = wxPay.unifiedOrder(map);
return result.get("code_url");
}
public static String queryStatus(String orderid) throws Exception {
WXPay wxPay = new WXPay(new MyWxConfig());
HashMap<String, String> map = new HashMap<>();
map.put("out_trade_no",orderid);
Map<String, String> result = wxPay.orderQuery(map);
return result.get("trade_state");
}
public static void main(String[] args) throws Exception {
String orderid = "202308241617987";
WXPay wxPay = new WXPay(new MyWxConfig());
HashMap<String, String> map = new HashMap<>();
map.put("out_trade_no",orderid);
Map<String, String> result = wxPay.orderQuery(map);
System.out.println(result);
}
}
MyWxConfig implements WXPayConfig
package com.qf.fmall2302.wxpay;
import com.github.wxpay.sdk.WXPayConfig;
import java.io.InputStream;
public class MyWxConfig implements WXPayConfig {
public static final String appid = "wx632c8f211f8122c6";
public static final String mch_id = "1497984412";
public static final String appkey = "sbNCm1JnevqI36LrEaxFwcaT0hkGxFnC";
@Override
public String getAppID() {
return appid;
}
@Override
public String getMchID() {
return mch_id;
}
@Override
public String getKey() {
return appkey;
}
@Override
public InputStream getCertStream() {
return null;
}
@Override
public int getHttpConnectTimeoutMs() {
return 0;
}
@Override
public int getHttpReadTimeoutMs() {
return 0;
}
}