【微信支付】Java微信支付流程

news2024/11/18 5:57:31

微信支付

微信支付流程

当我们需要支付一件商品时,首先从前端像后端传来商品ID,后端根据商品ID查询商品信息,然后封装订单信息,保存订单。下一步就是向微信远程调用支付接口,微信返回code_url,后端封装code_url返回前端,前端根据code_url显示二维码。

用户扫面二维码后进行支付,如果支付成功,就可以从微信端查询出支付信息。后端可以获取支付的状态(result_code),根据支付的状态修改订单信息。封装返回结果。

我们需要做的就是:

  1. 根据商品id查询商品信息。
  2. 封装成订单对象。
  3. 保存订单。
  4. 远程调用微信系统的接口。
  5. 封装code_url。

下单后的操作:

  1. 向微信端远程调用查询下单情况
  2. 根据状态result_code修改订单的状态。
  3. 封装结果

前端需要做的是:

  1. 点击下单按钮发送下单请求,根据返回的code_url显示支付二维码,加载定时器,每3秒查询一下支付状态
  2. 查询支付状态,如果为已支付,关闭二维码,关闭定时器,提示支付成功。

微信支付实现

1. 引入jar包

<!-- 微信需要的依赖 -->
<dependency>
	<groupId>com.github.wxpay</groupId>
	<artifactId>wxpay-sdk</artifactId>
	<version>0.0.3</version>
</dependency>

<!-- java端发送请求,在Java端模拟浏览器远程访问微信的接口 -->
<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
	<version>4.5.3</version>
</dependency>

2. 编写配置文件

配置文件主要为微信支付所需要的appid,mch_id,api_key。这是需要营业执照才可以申请的,个人无法获得
在这里插入图片描述

3. 代码模块

1. 解决跨域请求

package com.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 解决跨域问题
 */
@Configuration
public class CrossConfig implements WebMvcConfigurer {

    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                .allowCredentials(true)
                .maxAge(3600)
                .allowedHeaders("*");
    }
}

2. 远程访问工具类

package com.example.utils;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 远程访问工具类
 */
public class HttpClient {
    private String url;
    private Map<String, String> param;
    private int statusCode;
    private String content;
    private String xmlParam;
    private boolean isHttps;

    public boolean isHttps() {
        return isHttps;
    }

    public void setHttps(boolean isHttps) {
        this.isHttps = isHttps;
    }

    public String getXmlParam() {
        return xmlParam;
    }

    public void setXmlParam(String xmlParam) {
        this.xmlParam = xmlParam;
    }

    public HttpClient(String url, Map<String, String> param) {
        this.url = url;
        this.param = param;
    }

    public HttpClient(String url) {
        this.url = url;
    }

    public void setParameter(Map<String, String> map) {
        param = map;
    }

    public void addParameter(String key, String value) {
        if (param == null) {
            param = new HashMap<String, String>();
        }
        param.put(key, value);
    }

    public void post() throws ClientProtocolException, IOException {
        HttpPost http = new HttpPost(url);
        setEntity(http);
        execute(http);
    }

    public void put() throws ClientProtocolException, IOException {
        HttpPut http = new HttpPut(url);
        setEntity(http);
        execute(http);
    }

    public void get() throws ClientProtocolException, IOException {
        if (param != null) {
            StringBuilder url = new StringBuilder(this.url);
            boolean isFirst = true;
            for (String key : param.keySet()) {
                if (isFirst) {
                    url.append("?");
                } else {
                    url.append("&");
                }
                url.append(key).append("=").append(param.get(key));
            }
            this.url = url.toString();
        }
        HttpGet http = new HttpGet(url);
        execute(http);
    }

    /**
     * set http post,put param
     */
    private void setEntity(HttpEntityEnclosingRequestBase http) {
        if (param != null) {
            List<NameValuePair> nvps = new LinkedList<NameValuePair>();
            for (String key : param.keySet()) {
                nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数
            }
            http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数
        }
        if (xmlParam != null) {
            http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));
        }
    }

    private void execute(HttpUriRequest http) throws ClientProtocolException,
            IOException {
        CloseableHttpClient httpClient = null;
        try {
            if (isHttps) {
                SSLContext sslContext = new SSLContextBuilder()
                        .loadTrustMaterial(null, new TrustStrategy() {
                            // 信任所有
                            @Override
                            public boolean isTrusted(X509Certificate[] chain,
                                                     String authType)
                                    throws CertificateException {
                                return true;
                            }
                        }).build();
                SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                        sslContext);
                httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
                        .build();
            } else {
                httpClient = HttpClients.createDefault();
            }
            CloseableHttpResponse response = httpClient.execute(http);
            try {
                if (response != null) {
                    if (response.getStatusLine() != null) {
                        statusCode = response.getStatusLine().getStatusCode();
                    }
                    HttpEntity entity = response.getEntity();
                    // 响应内容
                    content = EntityUtils.toString(entity, Consts.UTF_8);
                }
            } finally {
                response.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            httpClient.close();
        }
    }

    public int getStatusCode() {
        return statusCode;
    }

    public String getContent() throws ParseException, IOException {
        return content;
    }
}

3. controller层的代码

package com.example.controller;

import com.example.domain.CommonResult;
import com.example.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;


/**
 * @Author shangtf
 * @Date 2023/7/25 15:18
 * @Description: TODO
 */
@RestController
@RequestMapping("order")
@CrossOrigin
public class OrderController {

    @Autowired
    private OrderService orderService;

    /**
     * 下单操作
     * @param orderId
     * @return
     */
    @PostMapping("createNative/{orderId}")
    private CommonResult createNative(@PathVariable String orderId) {

        return orderService.createNative(orderId);
    }

    /**
     * 查询订单状态
     * @param orderId
     * @return
     */
    @PostMapping("queryPayStatus/{orderId}")
    private CommonResult queryPayStatus(@PathVariable String orderId) {

        return orderService.queryPayStatus(orderId);
    }
}

service层

package com.example.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.domain.CommonResult;
import com.example.domain.Order;
import com.example.mapper.OrderMapper;
import com.example.service.OrderService;
import com.example.utils.HttpClient;
import com.github.wxpay.sdk.WXPayUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author shangtf
 * @Date 2023/7/25 15:21
 * @Description: TODO
 */
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private OrderMapper orderMapper;

    @Value("${weixin.appid}")
    private String appId;
    @Value("${weixin.mch_id}")
    private String mchId;

    @Value("${weixin.api_key}")
    private String apiKey;

    /**
     * 下单操作
     * @param orderId
     * @return
     */
    @Override
    public CommonResult createNative(String orderId) {
        //1. 先根据orderId查询出商品信息
        //2. 生成订单,添加到订单表。

        //这里直接从订单表中拿到一条订单(省略了上面步骤)
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("order_id",orderId);
        wrapper.eq("pay_status",0);
        Order order = orderMapper.selectOne(wrapper);

        if (!ObjectUtils.isEmpty(order)){

            try {

                Map<String,String> params = new HashMap<>();
                params.put("appid",appId);
                params.put("mch_id",mchId);
                params.put("nonce_str", WXPayUtil.generateNonceStr());
                params.put("body",order.getCourseTitle());
                params.put("out_trade_no",orderId);
                params.put("total_fee",new BigDecimal(0.01).multiply(new BigDecimal(100)).longValue()+"");
                //未来写成项目部署的ip
                params.put("spbill_create_ip","127.0.0.1");
                params.put("notify_url","http://localhost:9000/pay/back");
                params.put("trade_type","NATIVE");
                //创建Httpclient对象
                HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
                //支持https协议
                client.setHttps(true);
                //设置请求的参数
                client.setXmlParam(WXPayUtil.generateSignedXml(params,apiKey));
                //发送post请求
                client.post();
                //获取请求的响应结果
                String content = client.getContent();
                Map<String, String> map = WXPayUtil.xmlToMap(content);
                if (map.get("result_code").equals("SUCCESS")){
                    Map<String,Object> result = new HashMap<>();
                    result.put("codeUrl",map.get("code_url"));
                    result.put("price",order.getTotalFee());
                    result.put("orderNo",orderId);
                    return new CommonResult(200,"生成二维码成功",result);
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

        }
        return new CommonResult(500,"订单失效",null);
    }

    /**
     * 查询订单状态
     * @param orderId
     * @return
     */
    @Override
    public CommonResult queryPayStatus(String orderId) {

        try {
            HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
            Map<String,String> params = new HashMap<>();
            params.put("appid",appId);
            params.put("mch_id",mchId);
            params.put("out_trade_no",orderId);
            params.put("nonce_str",WXPayUtil.generateNonceStr());

            client.setHttps(true);
            client.setXmlParam(WXPayUtil.generateSignedXml(params,apiKey));
            client.post();

            String content = client.getContent();
            System.out.println("content = " + content);
            Map<String, String> map = WXPayUtil.xmlToMap(content);
            if (map.get("trade_state").equals("SUCCESS")){
                //1. 修改订单状态
                Order order = new Order();
                //修改订单状态为设为已支付,支付时间也可以设置
                order.setPayStatus(1);
                QueryWrapper<Order> wrapper = new QueryWrapper<>();
                wrapper.eq("order_id",orderId);
                wrapper.eq("pay_status",0);
                orderMapper.update(order,wrapper);
                //todo 2. 往支付记录表中添加支付记录
                return new CommonResult(200,"支付成功",null);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return  new CommonResult(500,"支付失败",null);
    }

}

4. mapper层

/**
 * @Author shangtf
 * @Date 2023/7/25 15:22
 * @Description: TODO
 */
@Mapper
public interface OrderMapper extends BaseMapper<Order> {

}

5. 前端页面

前端需要显示二维码,可以使用vue带的插件进行展示

npm install vue-qr  

使用如下:先引入vue-qr,然后components引用一下,最后使用<vue-qr>标签即可

<template>
  <div>
    <el-button type="primary" @click="pay">下单</el-button>

    <el-dialog
        title="下单"
        :visible.sync="centerDialogVisible"
        width="30%"
        v-model="payResult"
        center>

      <p  style=" width: 200px;padding: 10px;margin: 10px auto;">
        微信支付: {{ payResult.price }} 元
      </p>
      <div style="border: 1px solid #f3f3f3;width: 200px;padding: 10px;margin: 10px auto;">
        <vue-qr
            :text="payResult.codeUrl"
            :margin="0"
            :logoSrc="require('@/assets/logo.png')"
            colorLight="#fff"
            :size="200"
        >
        </vue-qr>
      </div>

      <span slot="footer" class="dialog-footer">
    <el-button @click="centerDialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="centerDialogVisible = false">确 定</el-button>
  </span>
    </el-dialog>

  </div>

</template>


<script>
import vueQr from "vue-qr"

export default {
  data() {
    return {
      centerDialogVisible: false,
      orderNo: "8624efa8396e4af7a912e7c4bf3fbb11",
      payResult: "",
      timer1:""
    };
  },
  methods: {
    pay() {
      this.centerDialogVisible = true
      this.$http.post("/order/createNative/" + this.orderNo).then(res => {
        if (res.data.code===200){
          this.payResult = res.data.data;

          this.timer1 = setInterval(()=>{
            this.queryPayStatus(this.payResult.orderNo)
          },3000);
        }
      })
    },
    queryPayStatus(){
      this.$http.post("/order/queryPayStatus/" + this.orderNo).then(res => {
       if (res.data.code===200){
         this.centerDialogVisible=false;
         clearInterval(this.timer1);
         this.timer1=null;
         this.$message({
           message: '支付成功',
           type: 'success'
         });
       }
      })
    }
  },
  components: {
    vueQr
  }
}


</script>


<style scoped>

</style>

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/791737.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vscode恢复被误删的文件(巧用本地历史记录)

背景&#xff1a;&#xff08;希望永远不要有这个背景&#xff09;使用vscode开发项目时&#xff0c;新建了文件&#xff0c;且文件没有git add、没有git stash、没有git commit。但是不小心点中了撤销更改&#xff08;新文件的撤销更改&#xff0c;其实就是删除该新文件&#…

【教学类-34-06】20230726拼图(“三角”凹凸拼图)3*4格子(中班主题《个别化拼图》偏美术)

图片展示&#xff1a; 圆形凹凸角变成三角形凹凸角&#xff0c;便于幼儿剪直线。 背景需求&#xff1a; 5月底&#xff0c;我自制凹凸角拼图&#xff08;动画片图片转拼图&#xff09;给幼儿裁剪&#xff0c;拼贴 教学实际操作时&#xff0c;发现圆形的凸角不适合幼儿裁剪&…

ROS 2 — 托管(生命周期)节点简介

一、说明 这篇文章是关于理解ROS 2中托管&#xff08;生命周期&#xff09;节点的概念。我们描述了概念性的想法以及我们为什么需要它。所以让我们开始吧&#xff01; 二、托管式节点 — 什么和为什么&#xff1f; 为了理解托管式节点&#xff0c;让我们从一个简单的问题陈述开…

微服务——服务异步通讯RabbitMQ

前置文章 消息队列——RabbitMQ基本概念容器化部署和简单工作模式程序_北岭山脚鼠鼠的博客-CSDN博客 消息队列——rabbitmq的不同工作模式_北岭山脚鼠鼠的博客-CSDN博客 消息队列——spring和springboot整合rabbitmq_北岭山脚鼠鼠的博客-CSDN博客 目录 Work queues 工作队列…

数据结构(c++实现)

数据结构 目录 数据结构1.链表实现单链表双链表 2.栈(先进后出&#xff0c;后进先出)3.单调栈4.队列&#xff08;先进先出&#xff09;5.单调队列6.小根堆操作 7.KMP8.Trie树(字典树) 1.链表实现 单链表 #include <iostream>using namespace std;const int N 100010;/…

AcWing 3708. 求矩阵的鞍点

输入样例&#xff1a; 3 4 1 2 3 4 1 2 3 4 1 2 3 4输出样例&#xff1a; 1 4 4 2 4 4 3 4 4 #include<bits/stdc.h> using namespace std; const int N1010; int n,m,a[N][N],x[N],y[N],flag1; int main(){scanf("%d%d",&n,&m);for(int i1;i<n;i…

抖音西瓜实时作品监控,一秒更新提醒

抖音西瓜实时作品监控&#xff0c;一秒更新提醒 安装必要的依赖库&#xff1a;使用pip安装aweme库。 pip install aweme 导入所需的库。 import datetime import time import schedule from aweme import API 创建一个函数&#xff0c;用于检查抖音作品是否更新。 def check_u…

端到端的视频编码方法及码率控制算法

文章目录 基于卷积神经网络的的端到端的视频编码方法自编码器 基于端到端学习的图像编码研究及进展变换量化熵编码 面向视频会议场景的 H.266/VVC 码率控制算法研究基于强化学习的视频码率自适应决策研究自适应流媒体传输技术码率自适应算法研究现状强化学习深度强化学习算法介…

mp4视频太大怎么压缩?教你轻松减小视频大小

MP4视频太大怎么办&#xff1f;很多人都会遇到这样的问题&#xff0c;MP4视频往因为画面清晰度高&#xff0c;画面流畅&#xff0c;所以视频文件会比较大&#xff0c;如果你想向朋友或者家人分享这个视频&#xff0c;但是又因为文件太大无法发送&#xff0c;那么怎么办呢&#…

可视化开发工具:让软件应用开发变得更轻松

一、前言 你是否为编程世界的各种挑战感到头痛&#xff1f;想要以更高效、简单的方式开发出专业级的项目&#xff1f; JNPF低代码工具正是你苦心寻找的产品&#xff01;它是一款专为稍微懂一点点编程思想的入门级人员设计的神奇工具&#xff0c;集成了丰富的功能和组件&#xf…

使用 CSS 自定义属性

我们常见的网站日夜间模式的变化&#xff0c;其实用到了 css 自定义属性。 CSS 自定义属性&#xff08;也称为 CSS 变量&#xff09;是一种在 CSS 中预定义和使用的变量。它们提供了一种简洁和灵活的方式来通过多个 CSS 规则共享相同的值&#xff0c;使得样式更易于维护和修改。…

深度剖析APP开发中的UI/UX设计

作为一个 UI/UX设计师&#xff0c;除了要关注 UI/UX设计之外&#xff0c;还要掌握移动开发知识&#xff0c;同时在日常工作中也需要对用户体验有一定的认知&#xff0c;在本次分享中&#xff0c;笔者就针对自己在工作中积累的一些经验来进行一个总结&#xff0c;希望能够帮助到…

暑假学生使用什么牌子台灯好?分享五款学生使用的台灯

临近暑假&#xff0c;是不是开始补课或写暑假作业了呢&#xff1f;是不是还在为选一款学生使用的台灯而发愁&#xff1f;今天小编就来给大家推荐几款台灯供大家参考参考。 那么问题来了&#xff0c;怎么选择合适的护眼台灯&#xff1f; 第一&#xff1a;先考虑个人预算选择适…

Modbus RTU通信应用

一、功能概述 1.1 概述 Modbus串行通信协议是Modicon公司在1970年开发的。 Modbus串行通信协议有Modbus ASCII和Modbus RTU两种模式&#xff0c;Modbus RTU协议通信效率较高&#xff0c;应用更加广泛。 Modbus RTU协议是基于RS232和RS485串行通信的一种协议&#xff0c;数据通…

论文解读|用于从RGB-D数据进行3D物体检测的Frustum PointNets

原创 | 文 BFT机器人 01 摘要 论文研究了室内和室外场景中基于RGBD数据的3D目标检测。论文的方法不仅仅依赖于3D方案&#xff0c;而是利用成熟的2D对象检测器和先进的3D深度学习进行对象定位&#xff0c;即使是小对象也能实现高效率和高召回。 直接在原始点云中学习&#xff0…

如何让GPT自己命令自己?榨干最后一丝智能,解放双手!

1.让GPT先别说话 2.接下来&#xff0c;看看它学的怎么样 使用成功了&#xff01;效果拔群&#xff01; 3.接下来&#xff0c;让他回答自己生成的指令&#xff1a; 效果比想象的还要好&#xff01;果然最懂GPT的还是它自己&#xff0c;生成的prompt比自己手写的prompt更加精准有…

rocketmq客户端本地日志文件过大调整配置(导致pod缓存cache过高)

现象 在使用rocketmq时&#xff0c;发现本地项目中文件越来越大&#xff0c;查找发现在/home/root/logs/rocketmqlog目录下存在大量rocketmq_client.log日志文件。 配置调整 开启slf4j日志模式&#xff0c;在项目启动项中增加-Drocketmq.client.logUseSlf4jtrue因为配置使用的…

Bug管理规范

目录 1.目的 2.角色和职责 3.缺陷等级定义 4.缺陷提交原则 5.缺陷流转流程 5.1创建缺陷 5.2缺陷分拣/分配 5.3研发认领缺陷 5.4.研发解决缺陷 5.5关闭缺陷 5.6缺陷激活 1.目的 项目过程中对缺陷管理的规则&#xff0c;明确提单规范、用例优先级的选择规则、走单流程、…

为Android构建现代应用——应用架构

选择风格(Choosing a style) 我们将依照Google在《应用架构指南》中推荐的最佳实践和架构指南来构建OrderNow的架构。 这些定义包括通过各层定义组件的一些Clean Architecture原则。 层次的定义(Definition of the layers) 在应用程序中&#xff0c;我们将定义以下主要层次…

【C++ 进阶】继承

一.继承的定义格式 基类又叫父类&#xff0c;派生类又叫子类&#xff1b; 二.继承方式 继承方式分为三种&#xff1a; 1.public继承 2.protected继承 3.private继承 基类成员与继承方式的关系共有9种&#xff0c;见下表&#xff1a; 虽然说是有9种&#xff0c;但其实最常用的还…