草庐IT

微信支付及支付回调

猫的幻想曲 2023-04-22 原文

1.微信支付

通过微信平台为商家提供代收款服务

1.1微信支付的业务--商户注册微信支付业务:

 

 

 1.2申请支付订单--商户向支付平台申请支付链接

支付订单,并不是用户提交的商品订单,而是商品向微信支付平台申请的支付链接

1.2.1导入微信支付的依赖

wxpay的maven依赖:

<dependency>
    <groupId>com.github.wxpay</groupId>
    <artifactId>wxpay-sdk</artifactId>
    <version>0.0.3</version>
</dependency>

1.2.2创建微信支付配置类,配置商品信息

创建一类,实现WXPayConfig接口

重写三分方法,分别设置商品AppID\商户ID\商户密钥

package com.qfedu.config;

import com.github.wxpay.sdk.WXPayConfig;

import java.io.InputStream;

/**
 * @Description:
 * @Author : Jerry
 * @create : 2022-07-02 18:16
 */
public class MyPayConfig implements WXPayConfig {
    @Override
    public String getAppID() {
        return "wx632c8f211f8122c6";
    }

    @Override
    public String getMchID() {
        return "1497984412";
    }

    @Override
    public String getKey() {
        return "sbNCm1JnevqI36LrEaxFwcaT0hkGxFnc";
    }

    @Override
    public InputStream getCertStream() {
        return null;
    }

    @Override
    public int getHttpConnectTimeoutMs() {
        return 0;
    }

    @Override
    public int getHttpReadTimeoutMs() {
        return 0;
    }
}

 1.2.3设置订单的参数

//设置当前订单信息
HashMap<String,String> data = new HashMap<>();
data.put("fee_type","CNY");         //支付币种
data.put("total_fee","0.1");        //支付总金额
data.put("body","咪咪虾条");        // 商品描述
//使用当前用户订单的编号作为当前支付交易的交易号
data.put("out_trade_no", orderId);
data.put("trade_type","NATIVE");    //交易类型
data.put("notify_url","/pay/success");          //设置支付完成时的回调方法接口

修改OrderService的实现类:

package com.qfedu.fmmall.service.impl;

import com.qfedu.fmmall.dao.OrderItemMapper;
import com.qfedu.fmmall.dao.OrdersMapper;
import com.qfedu.fmmall.dao.ProductSkuMapper;
import com.qfedu.fmmall.dao.ShoppingCartMapper;
import com.qfedu.fmmall.entity.OrderItem;
import com.qfedu.fmmall.entity.Orders;
import com.qfedu.fmmall.entity.ProductSku;
import com.qfedu.fmmall.entity.ShoppingCartVO;
import com.qfedu.fmmall.service.OrderService;
import com.qfedu.fmmall.vo.R;
import com.qfedu.fmmall.vo.ResStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;

/**
 * @Description:
 * @Author : Jerry
 * @create : 2022-07-01 17:46
 */
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private ShoppingCartMapper shoppingCartMapper;
    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private OrderItemMapper orderItemMapper;
    @Autowired
    private ProductSkuMapper productSkuMapper;

    /**
     * 保存订单业务
     * @param cids 在购物车列表页面选择得购物车记录的ID
     * @param order
     * @return
     */
    @Transactional
    public Map<String,String> addOrder(String cids, Orders order)  {

        Map<String,String> map = new HashMap<>();

        //1.校验库存:根据cids查询当前订单中关联的购物车记录详情(包括库存)
        String[] arr = cids.split(",");
        List<Integer> cidList = new ArrayList<>();
        for (int i = 0; i<arr.length; i++){
            cidList.add(Integer.parseInt(arr[i]));
        }
        List<ShoppingCartVO> list = shoppingCartMapper.selectShopCartByCids(cidList);

        boolean f = true;
        String untitled = "";
        for (ShoppingCartVO sc: list){
            if(Integer.parseInt(sc.getCartNum())>sc.getSkuStock()){
                f = false;
            }
            //获取所有商品名称,以,分割拼接称字符串
            untitled = untitled+sc.getProductName()+",";

        }
        if(f){
            //2. 保存订单
            //收货人信息:姓名,电话,地址,商品总价格,支付方式,订单创建时间
            //订单初始状态(待支付 1)
            order.setUntitled(untitled);
            order.setCreateTime(new Date());
            order.setStatus("1");
            //生成订单编号
            String orderId = UUID.randomUUID().toString().replace("-", " ");
            order.setOrderId(orderId);
            ordersMapper.insert(order);

            //3.生成商品快照
            for(ShoppingCartVO sc:list){
                int cnum = Integer.parseInt(sc.getCartNum());
                String itemId = System.currentTimeMillis()+""+(new Random().nextInt(89999)+10000);//增大容错率
                OrderItem orderItem = new OrderItem(itemId, orderId, sc.getProductId(), sc.getProductName(), sc.getProductImg(), sc.getSkuId(), sc.getSkuName(), new BigDecimal(sc.getSellPrice())
                        , cnum, new BigDecimal(sc.getSellPrice()*cnum), new Date(), new Date(), 0);
                orderItemMapper.insert(orderItem);
            }

            //4.扣减库存:根据套餐id修改套餐库存量
            for (ShoppingCartVO sc: list){
                String skuId = sc.getSkuId();
                int newStock = sc.getSkuStock() - Integer.parseInt(sc.getCartNum());

                //updateByExampleSelective只添加修改的,其他的不变
                ProductSku productSku = new ProductSku();
                productSku.setSkuId(skuId);
                productSku.setStock(newStock);
                productSkuMapper.updateByPrimaryKeySelective(productSku);
            }

            //5.删除购物车:当购物车中得记录购买成功之后,购物车中对应做删除操作
            for(int cid:cidList){
                shoppingCartMapper.deleteByPrimaryKey(cid);
            }

            map.put("orderId",orderId);
            map.put("productNames",untitled);
            return map;
        }else {
            //表示库存不足
            return null;
        }

    }
}

OrderController:(微信支付实现)

package com.qfedu.controller;

import com.github.wxpay.sdk.WXPay;
import com.qfedu.config.MyPayConfig;
import com.qfedu.fmmall.entity.Orders;
import com.qfedu.fmmall.service.OrderService;
import com.qfedu.fmmall.vo.R;
import com.qfedu.fmmall.vo.ResStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * @Description:
 * @Author : Jerry
 * @create : 2022-07-01 19:40
 */
@RestController
@CrossOrigin
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping("/add")
    public R add(String cids, @RequestBody Orders order){
        R r = null;
        try {
            Map<String, String> orderInfo = orderService.addOrder(cids, order);
            String orderId = orderInfo.get("orderId");

            if(orderId!=null){

                //设置当前订单信息
                HashMap<String,String> data = new HashMap<>();
                data.put("fee_type","CNY");         //支付币种
                data.put("total_fee",order.getActualAmount()*100+"");        //支付总金额
                data.put("body",orderInfo.get("productNames"));             // 商品描述
                //使用当前用户订单的编号作为当前支付交易的交易号
                data.put("out_trade_no", orderId);
                data.put("trade_type","NATIVE");    //交易类型
                data.put("notify_url","/pay/success");          //设置支付完成时的回调方法接口

                //发送请求,获取响应
                //微信支付:申请支付连接
                WXPay wxPay = new WXPay(new MyPayConfig());
                Map<String, String> resp = wxPay.unifiedOrder(data);
                orderInfo.put("payUrl",resp.get("code_url"));
                //orderInfo中包含了:订单编号,购买的商品名称,支付链接
                r = new R(ResStatus.OK,"提交订单成功!!!",orderInfo);
            }else {
                r = new R(ResStatus.NO,"提交订单失败!!!",null);
            }
        } catch (Exception e) {
            r = new R(ResStatus.NO,"提交订单失败!!!",null);
        }
        return r;
    }

}

2.支付回调

支付回调:当用户支付成功之后,支付平台会向我们指定的服务器接口发送请求传递订单支付状态数据

2.1 创建一个控制器定义回调接口

package com.qfedu.controller;

import com.github.wxpay.sdk.WXPayUtil;
import com.qfedu.fmmall.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description: 回调接口:当用户支付成功之后,微信支付平台就会请求这个接口,将支付状态的数据传递过来
 * @Author : Jerry
 * @create : 2022-07-02 18:44
 */
@RestController
@RequestMapping("/pay")
public class PayController {

    @Autowired
    private OrderService orderService;

    /**
     * 1.接收微信支付平台传递的数据(使用request的输入流接收)
     */
    @PostMapping("/callback")
    public String success(HttpServletRequest request) throws Exception {
        ServletInputStream is = request.getInputStream();
        byte[] bs = new byte[1024];
        int len = -1;
        StringBuilder builder = new StringBuilder();
        while ((len = is.read(bs)) != -1){
            builder.append(new String(bs,0,len));
        }
        String s = builder.toString();
        //使用帮助类将xml接口的字符串转换成map
        Map<String,String> map = WXPayUtil.xmlToMap(s);

        if(map!=null && "success".equalsIgnoreCase(map.get("result_code"))){
            //支付成功
            //2.修改订单状态为“待发货/已支付”
            String orderId = map.get("out_trade_no");
            int i = orderService.updateOrderStatus(orderId, "2");
            System.out.println("--orderId:"+orderId);
            //3.响应微信支付平台
            if(i>0){
                HashMap<String,String> resp = new HashMap<>();
                resp.put("return_code","success");
                resp.put("return_msg","OK");
                resp.put("appid",map.get("appid"));
                resp.put("result_code","success");
                return WXPayUtil.mapToXml(resp);
            }
        }
        //支付失败
        return null;
    }

}

2.2 设置回调URL

在订单接口中申请支付链接的时候将回调接口的路径设置给微信支付平台

 

 2.3 Ngrok实现内网穿透

网站:ngrok.cc

注册账号,申请隧道id

开通隧道:

 

 获取隧道id:

 下载客户端

2.4 前端通过轮询访问获取订单支付状态

流程图:

 

接口实现:

service接口:

package com.qfedu.fmmall.service;

import com.qfedu.fmmall.entity.Orders;
import com.qfedu.fmmall.vo.R;

import java.util.Map;

/**
 * @Description: 订单接口
 * @Author : Jerry
 * @create : 2022-07-01 17:45
 */
public interface OrderService {

    public Map<String,String> addOrder(String cids, Orders order);

    public int updateOrderStatus(String orderId,String status);

    public R getOrderById(String orderId);

}

service实现类:

@Override
public R getOrderById(String orderId) {
    Orders orders = ordersMapper.selectByPrimaryKey(orderId);
    return new R(ResStatus.OK,"success",orders.getStatus());
}

controller:

@GetMapping("/status/{oid}")
public R getOrderStatus(@PathVariable("oid") String orderId,
                        @RequestHeader("token")String token){
    R r = orderService.getOrderById(orderId);
    return r;
}

2.5 webSocket消息推送:

实现流程: 

 2.5.1 创建webSocket服务器

添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

添加websocket服务节点配置:(Java配置方式)

@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter getServerEndpointExporter(){
        return new ServerEndpointExporter();
    }

}

创建websocket服务器:

package com.qfedu.websocket;

import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;

import javax.websocket.OnClose;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description:
 * @Author : Jerry
 * @create : 2022-07-03 13:06
 */
@Component
@ServerEndpoint("/webSocket/{oid}")
public class WebSocketServer {

    private static ConcurrentHashMap<String,Session> sessionMap = new ConcurrentHashMap<>();

    /**
     * 前端发送请求建立websocket连接,就会执行  @OnOpen 方法
     */
    @OnOpen
    public void open(@PathParam("oid") String orderId, Session session){
        System.out.println("---------建立连接:"+orderId);
        sessionMap.put(orderId,session);
    }

    /**
     * 前端关闭页面或者主动关闭webSocket连接,都会执行close
     */
    @OnClose
    public void close(@PathParam("oid") String orderId){
        sessionMap.remove(orderId);
    }

    public static void sendMsg(String orderId,String msg){
        try {
            Session session = sessionMap.get(orderId);
            session.getBasicRemote().sendText(msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

修改回调接口:

 

有关微信支付及支付回调的更多相关文章

  1. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  2. ruby - 有人可以帮助解释类创建的 post_initialize 回调吗 (Sandi Metz) - 2

    我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法

  3. 微信小程序通过字典表匹配对应数据 - 2

    前言一般来说,前端根据后台返回code码展示对应内容只需要在前台判断code值展示对应的内容即可,但要是匹配的code码比较多或者多个页面用到时,为了便于后期维护,后台就会使用字典表让前端匹配,下面我将在微信小程序中通过wxs的方法实现这个操作。为什么要使用wxs?{{method(a,b)}}可以看到,上述代码是一个调用方法传值的操作,在vue中很常见,多用于数据之间的转换,但由于微信小程序诸多限制的原因,你并不能优雅的这样操作,可能有人会说,为什么不用if判断实现呢?但是if判断的局限性在于如果存在数据量过大时,大量重复性操作和if判断会让你的代码显得异常冗余。wxswxs相当于是一个独立

  4. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  5. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  6. ruby-on-rails - 与 ActiveMerchant 一起使用的最佳支付网关是什么? - 2

    我需要使用ActiveMerchant库在我们的一个Rails应用程序中设置支付解决方案。尽管这个问题非常主观,但人们对主要网关(BrainTree、Authorize.net等)的体验如何?它必须:处理定期付款。有能力记入个人帐户。能够取消付款。有办法存储用户的付款详细信息(例如Authotize.netsCIM)。干杯 最佳答案 ActiveMerchant很棒,但在过去一年左右的时间里,我在使用它时发现了一些问题。首先,虽然某些网关可能会得到“支持”——但并非所有功能都包含在内。查看功能矩阵以确保完全支持您选择的网关-http

  7. ruby-on-rails - 将保存回调添加到单个 ActiveRecord 实例,可以吗? - 2

    是否可以为单个ActiveRecord实例添加回调?作为进一步的限制,这是继续使用库,所以我无法控制该类(除了对其进行猴子修补)。这或多或少是我想做的:defdo_something_creazymessage=Message.newmessage.on_save_call:do_even_more_crazy_stuffenddefdo_even_more_crazy_stuff(message)puts"Message#{message}hasbeensaved!Hallelujah!"end 最佳答案 你可以通过在创建对象后立

  8. ruby-on-rails - Ruby method_added 回调不触发包括模块 - 2

    我想写一点“Deprecate-It”库并经常使用“method_added”回调。但是现在我注意到在包含模块时不会触发此回调。是否有任何回调或变通方法,以便在某些内容包含到自身时通知类“Foobar”?用于演示的小Demo:#IncludingModulswon'ttriggermethod_addedcallbackmoduleInvisibleMethoddefinvisible"Youwon'tgetacallbackfromme"endendclassFoobardefself.method_added(m)puts"InstanceMethod:'#{m}'addedto'

  9. ruby-on-rails - 使用 before_save 回调或自定义验证器添加验证错误? - 2

    我有一个模型Listingbelongs_to:user。或者,Userhas_many:listings。每个列表都有一个对其进行分类的类别字段(狗、猫等)。User还有一个名为is_premium的bool字段。这是我验证类别的方式...validates_format_of:category,:with=>/(dogs|cats|birds|tigers|lions|rhinos)/,:message=>'isincorrect'假设我只想让高级用户能够添加老虎、狮子和犀牛。我该怎么做?最好在before_save方法中执行此操作吗?before_save:premium_che

  10. Ubuntu20.04系统WineHQ7.0安装微信 - 2

    提供3种Ubuntu系统安装微信的方法,在Ubuntu20.04上验证都ok。1.WineHQ7.0安装微信:ubuntu20.04安装最新版微信--可以支持微信最新版,但是适配的不是特别好;比如WeChartOCR.exe报错。2.原生微信安装:linux系统下的微信安装(ubuntu20.04)--微信适配的最好,反应最快,但是微信版本只到2.1.1,版本太老,很多功能都没有。3.深度deepin-wine6安装微信:ubuntu20.04+系统deepin-wine6安装新版微信--综合比较好,当前个人使用此种方法1个月,微信版本3.4;没什么大问题,尚可。一、WineHQ7.0安装微信

随机推荐