乐优商城项目总结——12 微信支付(前台接收数据和微信支付相关配置)

下单支付

商城的主线剧情到这就要结束了。

实现步骤:

我先从前台数据接收和下单讲。

1 接收前台商品和用户数据(用户点确认支付,到支付详情页时)

前台传来的数据需要进行校验,从数据库里查一查传来的价格是否和数据库一样,不然可能被某些人篡改,改成一分钱付款,那就赔的裤头都没了。

1.1 DTO类用来接收前台传来的信息,然后转为对象:

CartDTO :

@Data
@NoArgsConstructor
@AllArgsConstructor
public class CartDTO {
    private Long skuId;//商品skuid
    private Integer num;    //购买数量
}

OrderDTO:
前台数据传到这里来

/**
 * 用来接收前台的数据
 *DTO: datatransferobject,数据转成对象
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderDTO {
    @NotNull
    private Long addressId; //收货人地址id
    @NotNull
    private Integer paymentType;    //付款类型
    @NotNull
    private List<CartDTO> carts;      //订单详情
}

1.2 在这里进行订单校验和创建订单
service:

  @Transactional
    public Long createOrder(OrderDTO orderDTO) {

//        1.新增订单
        Order order = new Order();
//        1.1 订单编号,基本信息
        long orderId = idWorker.nextId();
        order.setOrderId(orderId);      //order的id,上面生成了
        order.setCreateTime(new Date());    //order的创建时间
        order.setPaymentType(orderDTO.getPaymentType());    //order的付款方式,从前台页面获取
//        1.2 用户信息
        UserInfo userInfo = CartFilter.getUserInfo();
        order.setUserId(userInfo.getId());  //购买人id,从cookie中获取信息,解析后放到线程变量中获取
        order.setBuyerNick(userInfo.getUsername()); //购买人名字
        order.setBuyerRate(false);  //评价
//        1.3 收货人地址(假的)
        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());
//        1.4 金额
//        把cartDTO转为一个map,key是sku的id,值是num
        Map<Long, Integer> collect = orderDTO.getCarts().stream().collect(Collectors.toMap(CartDTO::getSkuId, CartDTO::getNum));
//        取出sku id
        Set<Long> ids = collect.keySet();
//        从数据库中查询skus
        List<Sku> skus = goodsClient.querySkuByIds(new ArrayList<>(ids));
//        准备orderdetail集合
        ArrayList<OrderDetail> details = new ArrayList<>();
        Long totalPay = 0L;
        for (Sku sku : skus) {
            totalPay += sku.getPrice() * collect.get(sku.getId());
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setImage(StringUtils.substringBefore(sku.getImages(), ","));
            orderDetail.setNum(collect.get(sku.getId()));
            orderDetail.setOwnSpec(sku.getOwnSpec());
            orderDetail.setPrice(sku.getPrice());
            orderDetail.setSkuId(sku.getSpuId());
            orderDetail.setOrderId(orderId);
            orderDetail.setTitle(sku.getTitle());
            details.add(orderDetail);
        }
        order.setTotalPay(totalPay);
//        实付金额 商品金额+邮费-优惠
//        System.out.println("总金额:" + totalPay + "邮费" + order.getPostFee());
        order.setActualPay(totalPay/* + order.getPostFee()*/);
//        1.5 order写入数据库,有选择性的新增,没填的让他走数据库中的默认值
        int count = orderMapper.insertSelective(order);
        if (count != 1) {
            log.error("创建订单失败,orderId:{}", orderId);
            throw new LyException(ExceptionEnum.CREATE_ORDER_ERROR);
        }
//        2 新增订单详情
        int count2 = detailMapper.insertList(details);
        if (count != details.size()) {
            log.error("创建订单详情失败,orderId:{}", orderId);
            throw new LyException(ExceptionEnum.CREATE_ORDER_ERROR);
        }
//        3 新增订单状态
        OrderStatus orderStatus = new OrderStatus();
        orderStatus.setCreateTime(order.getCreateTime());
        orderStatus.setOrderId(orderId);
        orderStatus.setStatus(OrderStatusEnum.UNPAY.value());
        int count3 = statusMapper.insertSelective(orderStatus);
        if (count3 != 1) {
            log.error("创建订单状态失败,orderId:{}", orderId);
            throw new LyException(ExceptionEnum.CREATE_ORDER_ERROR);
        }
//        4 减库存
        List<CartDTO> cartDTOS = orderDTO.getCarts();
        goodsClient.decreaseStock(cartDTOS);
        return orderId;
    }

创建订单时有idWorker工具类,用来生成商品id编号,编号不会重复,可以自己上网搜一个编号生成器。
到这里订单信息就已经存到数据库中了,该处理交钱的事了。

2 微信支付的相关配置

流程是:

  1. 用户点确认支付后根据商品id创建一个支付订单。
  2. 用户支付订单(这一步微信处理,我们不用管)。
  3. 当用户处理完后会给用户反馈付款成功,也会给我们(商家)发送一条信息(通过通知回调地址),提示用户已付款让我们进行下一步操作。(必须做出处理回应接收成功了,不然微信会一直发这条消息给我们)
  4. 我们给用户发货。

先配置一下微信支付:

2.1 把微信需要的配置放到yml:
6个配置分别是:
公众账号ID(我胡乱改了,不能拿来直接用的,需要的话自己申请一个吧)
商户号(也改了)
生成签名的密钥
连接超时时间
读取超时时间
下单通知回调地址 (自己申请的,下篇会讲到)

  pay:
    appId: wx8397f8696b531232
    mchId: 1473426331
    key: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
    connectTimeoutMs: 5000
    readTimeoutMs: 10000
    notifyUrl: http://h7j4zz.natappfree.cc/notify/pay

2.2 用config类,指明这些都是什么:

@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;// 下单通知回调地址

    /**
     * 获取商户证书内容
     *
     * @return 商户证书内容
     */
    @Override
    public InputStream getCertStream() {
        return null;
    }
}

2.3 将这些传入到微信的配置中:

@Configuration
public class PayConfiguration {

    @Bean
    @ConfigurationProperties(prefix = "ly.pay")
    public PayConfig payConfig() {
        return new PayConfig();
    }

    @Bean
    public WXPay wxPay(PayConfig payConfig) {
        return new WXPay(payConfig, WXPayConstants.SignType.HMACSHA256);
    }
}

2.4 创建微信支付订单:
这里面的需要三个参数:
1,商品描述信息(用户支付时能看到买的什么)
2,商品订单编号
3,总金额
有一些其它的配置前面 PayConfig已经传入,这里调用就好,有一些其他的配置根据自己需要改动。

@Slf4j
@Component
public class PayHelper {
    @Autowired
    private WXPay wxPay;

    @Autowired
    private PayConfig payConfig;


    public String getPayUrl(String desc, long orderId, long totalPay) {
        // 准备请求参数
        Map<String, String> data = new HashMap<>();
//        商品描述
        data.put("body", desc);
//        订单号
        data.put("out_trade_no", String.valueOf(orderId));
//        金额,单位是分
        data.put("total_fee", String.valueOf(totalPay));
//        调用微信支付的终端ip
        data.put("spbill_create_ip", "127.0.0.1");
//        回调地址
        data.put("notify_url", payConfig.getNotifyUrl());
//        交易类型为扫码支付
        data.put("trade_type", "NATIVE");  // 此处指定为扫码支付

        try {
            // 发起请求
            Map<String, String> result = wxPay.unifiedOrder(data);
            // 校验业务标示
            checkResultCode(result);
            // 取出支付链接地址,并返回
            String url = result.get("code_url");
            if (StringUtils.isBlank(url)) {
                throw new RuntimeException("【微信支付】下单支付链接为空");
            }
            return url;
        } catch (Exception e) {
            // 统一下单失败
            log.error("【支付助手】统一下单失败,失败原因:{}", e.getMessage(), e);
            throw new LyException(ExceptionEnum.WX_PAY_ORDER_FAIL);
        }
    }

    /**
     * 判断业务标识,记录错误原因
     *
     * @param resp
     */
    public void checkResultCode(Map<String, String> resp) {
        // 判断业务执行结果
        if (WXPayConstants.FAIL.equals(resp.get("result_code"))) {
            // 下单业务失败,
            log.error("【支付助手】统一下单失败,错误码:{},错误原因:{}",
                    resp.get("err_code"), resp.get("err_code_des"));
            throw new LyException(ExceptionEnum.WX_PAY_ORDER_FAIL);

        }
    }
	 	/**
	     * 校验签名
	     *
	     * @param resp
	     */
    public void checkSignature(Map<String, String> data) {
        try {
            boolean boo1 = WXPayUtil.isSignatureValid(data, payConfig.getKey(), WXPayConstants.SignType.MD5);
            boolean boo2 = WXPayUtil.isSignatureValid(data, payConfig.getKey(), WXPayConstants.SignType.HMACSHA256);
            if (!boo1 && !boo2) {
                throw new RuntimeException();
            }
        } catch (Exception e) {
            // 校验失败
            log.error("【支付助手】签名校验失败!");
            throw new RuntimeException("非法签名!", e);
        }
    }
}

到这里,让用户交钱的配置已经完成,下一篇让他们交钱。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值