java接入微信支付

java接入微信支付

一、环境

1.1、微信接口文档

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1

1.2、pom依赖

微信demo文档:https://pay.weixin.qq.com/wiki/doc/api/download/WxPayAPI_JAVA.zip

        
        <dependency>
            <groupId>com.github.wxpaygroupId>
            <artifactId>wxpay-sdkartifactId>
            <version>0.0.3version>
        dependency>

1.3、技术栈

前端:vue
后端:springboot、mybatis-plus

二、二维码生成器步骤

2.1、WXPay方法

com.github.wxpay.sdk.WXPay类下提供了对应的方法:

方法名 说明
microPay 刷卡支付
unifiedOrder 统一下单
orderQuery 查询订单
reverse 撤销订单
closeOrder 关闭订单
refund 申请退款
refundQuery 查询退款
downloadBill 下载对账单
report 交易保障
shortUrl 转换短链接
authCodeToOpenid 授权码查询openid

2.2、编写Controller层

package com.leo.fengmiweb.controller;

import com.github.wxpay.sdk.WXPay;
import com.leo.fengmiweb.config.MyWXPayConfig;
import com.leo.mall.pojo.ResultData;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
@RequestMapping("orders")
public class OrderController {

    @GetMapping("payInfo")
    public ResultData getPayInfo(){
        System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");
        try {

            MyWXPayConfig myWXPayConfig = new MyWXPayConfig();
            //导入配置文件
            WXPay wxPay = new WXPay(myWXPayConfig);

            //封装发送的数据
            HashMap<String, String> data = new HashMap<>();
            data.put("body", "leo发起支付");
            data.put("out_trade_no", "2016090910595900004012");
            data.put("fee_type", "CNY");    //人民币
            data.put("total_fee", "1");  //支付金额
            //data.put("spbill_create_ip", "123.12.12.123");
            data.put("notify_url", "/notifyPayInfo");    //回调地址
            data.put("trade_type", "NATIVE");  // 此处指定为扫码支付
            //data.put("product_id", "111");   //产品id信息

            //向微信发起请求
            Map<String, String> resultMap = wxPay.unifiedOrder(data);
            //给前端返回的数据(成功返回)
            return new ResultData(0,"下单成功",resultMap.get("code_url"));

        } catch (Exception e) {
            e.printStackTrace();
        }
        //给前端返回的数据(失败返回)
        return new ResultData(100,"下单失败");
    }
}

2.3、配置类

package com.leo.fengmiweb.config;

import com.github.wxpay.sdk.WXPayConfig;

import java.io.InputStream;

public class MyWXPayConfig implements WXPayConfig {
    @Override
    public String getAppID() {
        return "wx632cf211f8122xxx";
    }

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

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

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

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

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

2.3、测试(成功)

java接入微信支付_第1张图片

三、支付回调

3.1、conroller层回调接口

调用回调接口当支付成功向微信返回success

package com.leo.fengmiweb.controller;

import com.github.wxpay.sdk.WXPayUtil;
import com.leo.fengmiweb.websocket.WebSocketServer;
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;

@RestController
public class PayController {

    @RequestMapping("/payNotify")
    public String payNotify(HttpServletRequest httpServletRequest){
        //获取Xml数据
        StringBuffer stringBuffer = new StringBuffer();
        try {
            ServletInputStream inputStream = httpServletRequest.getInputStream();
            byte[] bytes = new byte[1024];
            int len;
            while ((len = inputStream.read(bytes)) != -1){
                stringBuffer.append(new String(bytes,0,len));
            }
            Map<String, String> map = WXPayUtil.xmlToMap(stringBuffer.toString());
            if (map.get("result_code").equals("SUCCESS")){
                System.out.println(map);
                //websocket返回
                WebSocketServer.onMessage("4001","支付成功");

                //支付成功给微信返回SUCCESS
                HashMap<String, String> hashMap = new HashMap<>();
                hashMap.put("result_code","SUCCESS");
                hashMap.put("return_msg","SUCCESS");
                hashMap.put("appid",map.get("appid"));
                hashMap.put("return_code","SUCCESS");
                return WXPayUtil.mapToXml(hashMap);
            }else {
                System.out.println("支付失败");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

3.2、config层

配置websocket与前端进行通信

package com.leo.fengmiweb.config;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@SpringBootConfiguration
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

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

3.3、websocket层

编写websocket通信代码

package com.leo.fengmiweb.websocket;

import com.leo.mall.pojo.ResultData;
import org.springframework.stereotype.Component;

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

@Component
@ServerEndpoint("/WebSocketServer/{orderId}")
public class WebSocketServer {
    private static ConcurrentHashMap<String, Session> concurrentHashMap = new ConcurrentHashMap<>()

	//当创建socket时调用,创建一个session会话
    @OnOpen
    public void onOpen(@PathParam("orderId") String orderId, Session session) {
        concurrentHashMap.put(orderId, session);
    }

	//当关闭socket时调用,关闭当前会话
    @OnClose
    public void onClose(@PathParam("orderId") String orderId, Session session) {
        concurrentHashMap.remove(orderId);
    }

	//给前端返回msg数据
    public static void onMessage(String orderId, String msg) {
    	//创建当前session会话
        Session session = concurrentHashMap.get(orderId);
        try {
        	//给session会话返回一条数据
            session.getBasicRemote().sendText(msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.4、vue前端代码

3.4.1、api层

axios接口代码

import request from '@/utils/request'

export function getCodeUrl() {
  return request({
    url: `/orders/payInfo`,
    method: 'get'
  })
}

3.4.2、view层

前端页面代码

<template>
    <div>
       <Header>Header>
        <common-header>
            <template v-slot:title>
                我的购物车
            template>
        common-header>
        <el-divider>el-divider>
        <div class="pay">
            <el-card class="box-card" >
            <div slot="header" class="clearfix">
                <h3>请扫码支付h3>
            div>
            <div >
                <div style="display:flex; flex-direction:column;align-items: center;">
                    <span style="margin-bottom:30px;font-size:18px">订单将在<b style="color:red;font-size:20px">30分钟b>后关闭span>
                    
                    <div ref="refCode">div>
                    <el-button type="danger" round style="width:160px;margin-top:30px">取消支付el-button>
                div>
            div>
        el-card>
        div>

    div>
template>

<script>
import CommonHeader from '../components/CommonHeader.vue'
import Header from '@/components/Index/Header'
import {getCodeUrl} from '@/api/OrderPay'
import QRCode from 'qrcodejs2';

export default {
   components: {
        CommonHeader,
        Header
    },
    data: () => ({
        code_url: '',
        orderId: '4001',
    }),
    methods:{
        getPayUrl(){
            let _this = this;
            getCodeUrl().then(res =>{
                _this.code_url = res.data;
                window.console.log(_this.code_url);
                this.createQRCode(_this.code_url);
            })
        },
        //二维码
        createQRCode(code_url){
            new QRCode(this.$refs.refCode,{	//给div块返回二维码数据
                text:code_url,
                width:200,
                height:200,
                colorDark:'#000000',
                colorLight:'#ffffff',
                correctLevel:QRCode.CorrectLevel.H
            })
        }
    },
    mounted:function(){
        this.getPayUrl();

        let _this = this;

        //跟服务端建立websocket连接
        var webSocket = new WebSocket("ws://localhost:9090/WebSocketServer/" + this.orderId);	//ws协议
        //监听socket
        webSocket.onmessage = function(event){
            var msg = event.data;
            if (msg == "支付成功"){
                _this.$message.success("支付成功,3秒后跳转新页面。")
                window.setTimeout(function(){	//3秒后执行页面跳转
                    _this.$router.push({	//路由跳转
                        path:'my-order'
                    },30000)	//3秒
                })
            }
        }
    }

}
script>

<style scoped>
    .pay{
        width: 60%;
        margin: auto;
    }

style>

3.5、测试(成功)

java接入微信支付_第2张图片
java接入微信支付_第3张图片

四、FQA

4.1、依赖冲突

Caused by: java.lang.IllegalArgumentException: LoggerFactory is not a Logback LoggerContext but Logback is on the classpath. Either remove Logback or the competing implementation (class org.slf4j.impl.Log4jLoggerFactory loaded from file:/E:/IT_zhengqing/soft/soft-dev/Maven/repository-zhengqing/org/slf4j/slf4j-log4j12/1.7.30/slf4j-log4j12-1.7.30.jar). If you are using WebLogic you will need to add 'org.slf4j' to prefer-application-packages in WEB-INF/weblogic.xml: org.slf4j.impl.Log4jLoggerFactory
	at org.springframework.util.Assert.instanceCheckFailed(Assert.java:699)
	at org.springframework.util.Assert.isInstanceOf(Assert.java:599)
	at org.springframework.boot.logging.logback.LogbackLoggingSystem.getLoggerContext(LogbackLoggingSystem.java:284)
	at org.springframework.boot.logging.logback.LogbackLoggingSystem.beforeInitialize(LogbackLoggingSystem.java:104)
	at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationStartingEvent(LoggingApplicationListener.java:232)
	at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:213)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
	at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
	at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:74)
	at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:47)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:305)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
	at com.zhengqing.system.SystemApplication.main(SystemApplication.java:15)
	... 5 more

解决办法:
删除slf4j-log4j依赖文件

4.2、Remote host closed connection during handshake报错

System Exception:javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake
    at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:992)
    at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:396)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:355)
    at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
    at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:373)
    at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:381)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:111)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
    at com.ms.config.wx.sdk.WeChatUtil.processHttpPost(WeChatUtil.java:555)
    at com.ms.config.wx.sdk.WeChatUtil.unifiedorder(WeChatUtil.java:135)
    at com.ms.tpp.service.handler.wechat.WechatMiniProgramPaymentServiceImpl.pay(WechatMiniProgramPaymentServiceImpl.java:78)

解决办法:
第一行添加 System.setProperty(“https.protocols”, “TLSv1,TLSv1.1,TLSv1.2”);

你可能感兴趣的:(java,微信,开发语言)