一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

SpringBoot + vue 實現(xiàn)支付寶掃碼支付功能

 lincaomei 2022-04-08

簡言:

    前端時間在自己做的小項目上添加了ZFB的支付功能,并且優(yōu)化了網頁版支付寶的掃碼支付,使用的框架是Spring + SpringBoot + SpringMVC + Mybatis + VUE。

準備:

    首先需要到支付寶官網申請沙箱測試的資格:https://open.alipay.com/platform/home.htm,

點擊 查看接入文檔 根據(jù)自己的操作系統(tǒng)下載密鑰生成器,生成應用私鑰

步驟一:

    引入支付寶的Jar包

<!--    支付寶 jar-->

    <dependency>

      <groupId>com.alipay.sdk</groupId>

      <artifactId>alipay-sdk-java</artifactId>

      <version>4.22.67.ALL</version>

    </dependency>

步驟二:

    創(chuàng)建支付寶配置類

package org.lpy.config;

import com.alipay.api.AlipayApiException;

import com.alipay.api.AlipayClient;

import com.alipay.api.DefaultAlipayClient;

import com.alipay.api.internal.util.AlipaySignature;

import lombok.extern.log4j.Log4j2;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import javax.servlet.http.HttpServletRequest;

import java.util.HashMap;

import java.util.Map;

/**

 * 支付寶接口配置類

 * @author 林草莓233

 * @since 2022/04/08

 */

@Log4j2

@Configuration

public class PayConfig {

    // 請?zhí)顚懩腁ppId(必填)

    public static final String appID = "";

    //應用私鑰,這里修改生成的私鑰即可(必填)

    public static final String privateKey = "";

    //支付寶公鑰,不是應用公鑰!!!(必填)

    public static final String publicKey = "";

    //默認即可(必填)

    public static final String charset = "utf-8";

    //默認即可(必填)

    public static final String signType = "RSA2";

    @Bean

    public AlipayClient alipayClient(){

        //沙箱環(huán)境使用https://openapi./gateway.do,線上環(huán)境使用https://openapi.alipay.com/gateway.do

        return new DefaultAlipayClient("https://openapi./gateway.do", appID, privateKey, "json", charset, publicKey, signType);

    }

    /**

     * 驗簽,是否正確

     */

    public static boolean checkSign(HttpServletRequest request){

        Map<String, String[]> requestMap = request.getParameterMap();

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

        requestMap.forEach((key, values) -> {

            StringBuilder str = new StringBuilder();

            for(String value : values) {

                str.append(value);

            }

            log.info("ZFB驗簽:" + key + "===>" + str);

            paramsMap.put(key, str.toString());

        });

        //調用SDK驗證簽名

        try {

            return AlipaySignature.rsaCheckV1(paramsMap, PayConfig.publicKey, PayConfig.charset, PayConfig.signType);

        } catch (AlipayApiException e) {

            // TODO Auto-generated catch block

            e.printStackTrace();

            log.info("*********************驗簽失敗********************");

            return false;

        }

    }

}


步驟三:

    創(chuàng)建WeSorcket通道,用來通知前端支付狀態(tài)

package org.lpy.util;

import lombok.extern.slf4j.Slf4j;

import org.springframework.context.annotation.Bean;

import org.springframework.stereotype.Component;

import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import javax.websocket.OnClose;

import javax.websocket.OnMessage;

import javax.websocket.OnOpen;

import javax.websocket.Session;

import javax.websocket.server.ServerEndpoint;

import java.io.IOException;

import java.util.concurrent.CopyOnWriteArraySet;

@Component

@ServerEndpoint("/webSocket")

@Slf4j

public class WebSocket {

    private Session session;

    private static CopyOnWriteArraySet<WebSocket> webSockets = new CopyOnWriteArraySet<>();

    /**

     * 新建webSocket配置類

     * @return

     */

    @Bean

    public ServerEndpointExporter serverEndpointExporter() {

        return new ServerEndpointExporter();

    }

    /**

     * 建立連接

     * @param session

     */

    @OnOpen

    public void onOpen(Session session) {

        this.session = session;

        webSockets.add(this);

        log.info("【新建連接】,連接總數(shù):{}", webSockets.size());

    }

    /**

     * 斷開連接

     */

    @OnClose

    public void onClose(){

        webSockets.remove(this);

        log.info("【斷開連接】,連接總數(shù):{}", webSockets.size());

    }

    /**

     * 接收到信息

     * @param message

     */

    @OnMessage

    public void onMessage(String message){

        log.info("【收到】,客戶端的信息:{},連接總數(shù):{}", message, webSockets.size());

    }

    /**

     * 發(fā)送消息

     * @param message

     */

    public void sendMessage(String message){

        log.info("【廣播發(fā)送】,信息:{},總連接數(shù):{}", message, webSockets.size());

        for (WebSocket webSocket : webSockets) {

            try {

                webSocket.session.getBasicRemote().sendText(message);

            } catch (IOException e) {

                log.info("【廣播發(fā)送】,信息異常:{}", e.fillInStackTrace());

            }

        }

    }

}

步驟四:

    創(chuàng)建交易控制中心(AliPayHandler)

package org.lpy.handler;

import com.alipay.api.AlipayApiException;

import com.alipay.api.AlipayClient;

import com.alipay.api.request.AlipayTradePrecreateRequest;

import com.alipay.api.response.AlipayTradePrecreateResponse;

import lombok.extern.slf4j.Slf4j;

import org.lpy.config.PayConfig;

import org.lpy.pojo.AliReturnPayBean;

import org.lpy.util.WebSocket;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.io.IOException;

import java.math.BigDecimal;

/**

 * 支付交易控制中心

 * @author 林草莓233

 * @since 2022/04/08

 */

@Controller

@Slf4j

public class AliPayHandler {

    @Resource

    private AlipayClient alipayClient;

    @Resource

    private WebSocket webSocket;

    @Value("${company}")

    private String company;

    @Value("${timeout}")

    private String timeout;

    @RequestMapping("/createQR")

    @ResponseBody

    public String send(BigDecimal money,String title) throws AlipayApiException {

        AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); //創(chuàng)建API對應的request類

        request.setNotifyUrl("http://127.0.0.1:8081/call");

        //同步回調地址

//  request.setReturnUrl("");

        request.setBizContent( "{"   +

                "\"out_trade_no\":\""+ System.currentTimeMillis()/1000 + Math.round((Math.random()+1) * 1000) + "\"," + // 商戶訂單號

                "\"total_amount\":\""+ money +"\"," +// 商品價格

                "\"subject\":\""+ title +"\"," +// 商品標題

                "\"store_id\":\"" + company + "\"," +    // 組織或公司名

                "\"timeout_express\":\"" + timeout + "\"}" );    //支付超時時間

        AlipayTradePrecreateResponse response = alipayClient.execute(request);

        if (response.isSuccess()) {

            log.info("支付API調用成功");

            return response.getQrCode();

        } else {

            log.info("支付API調用失敗");

        }

        return "";

    }

    // 支付寶回調函數(shù)

    @RequestMapping("/call")

    public void call(HttpServletRequest request, HttpServletResponse response, AliReturnPayBean returnPay) throws IOException {

        response.setContentType("type=text/html;charset=UTF-8");

        log.info("支付寶的的回調函數(shù)被調用");

        if (!PayConfig.checkSign(request)) {

            log.info("驗簽失敗");

            response.getWriter().write("failture");

            return;

        }

        if (returnPay == null) {

            log.info("支付寶的returnPay返回為空");

            response.getWriter().write("success");

            return;

        }

        log.info("支付寶的returnPay" + returnPay);

        //表示支付成功狀態(tài)下的操作

        if (returnPay.getTrade_status().equals("TRADE_SUCCESS")) {

            log.info("支付寶的支付狀態(tài)為TRADE_SUCCESS");

            //業(yè)務邏輯處理 ,webSocket在下面會有介紹配置

            webSocket.sendMessage("true");

        }

        response.getWriter().write("success");

    }

}

request.setNotifyUrl("http://127.0.0.1:8081/call");這里要注意?。。?!

    這里的地址是錯誤的,這里應該要填寫外網可以訪問到的地址后面拼接上應用端口號加上“/call”,這樣支付寶才能調用到我們的call方法,返回支付狀態(tài),這樣因為隱私問題,我用內網的地址代替外網,如果同學們要部署在服務器上,這樣應該填寫服務器的公網IP加上"/call",例如你的公網IP為:123.45.6.7,應用端口號為:8081,回調地址應該填"http://123.45.6.7:8081/call",同樣的,之前在支付寶沙箱頁面也需要修改回調地址,兩個保持一致。如果同學們要在本地測試,則需要通過內網穿透來實現(xiàn)支付寶的回調,我會把內網穿透的方法放在最后。

步驟五:

    前端頁面,這里使用的是 VUE 框架 + element 組件 + qr 二維碼生成組件

    先通過命令加載 qr 組件:

npm install vue-qr --save

    前端頁面代碼:

<template>

<div>

<!-- 支付按鈕,模擬支付操作 -->

<van-button type="primary" @click="pay">支付</van-button>

<el-dialog :title="paySucc?'支付成功':'掃碼支付'" :visible.sync="dialogVisible" width="16%" center>

<!-- 生成二維碼圖片 -->

<vueQr :text="text" :size="200" v-if="!paySucc"></vueQr>

<!-- 使用websocket監(jiān)控是否掃描,掃描成功顯示成功并退出界面 -->

<span class="iconfont icon-success" style="position: relative;font-size: 100px;color:#42B983;margin-left: 50px;top:-10px;" v-else></span>

</el-dialog>

</div>

</template>

<script>

import vueQr from 'vue-qr'

export default {

data() {

return {

dialogVisible: false,

text: "",

paySucc: false

}

},

components: {

vueQr

},

methods: {

pay() {

let _this = this;

_this.paySucc = false;

_this.dialogVisible = true;

this.axios.request("http://localhost:8081/createQR")

.then((response) => {

_this.text = response.data;

_this.dialogVisible = true;

//使用webSocket發(fā)送請求,下面會簡單介紹websocket使用

if ("WebSocket" in window) {

// 打開一個 web socket

var ws = new WebSocket("ws://localhost:8081/bindingRecord");

ws.onopen = function() {

// Web Socket 已連接上,使用 send() 方法發(fā)送數(shù)據(jù)

// ws.send("data");

// alert("數(shù)據(jù)發(fā)送中...");

};

ws.onmessage = function(evt) {

var received_msg = evt.data;

// alert("數(shù)據(jù)已接收..." + evt.data);

if (Boolean(evt.data)) {

_this.paySucc = true;

setTimeout(() => {

_this.dialogVisible = false;

}, 3 * 1000);

}

ws.close();

};

ws.onclose = function() {

// // 關閉 websocket

console.log("連接已關閉...");

};

} else {

// 瀏覽器不支持 WebSocket

alert("您的瀏覽器不支持 WebSocket!");

}

}).catch((err) => {

console.log(err)

})

},

back(dataUrl, id) {

console.log(dataUrl, id)

}

}

}

</script>

<style>

.btn {

margin-left: 100px;

}

</style>

示例:

附言:

    實現(xiàn)內網穿透我們需要用到專門的工具,這里有兩種,分別是 Sunny-Ngrok 和 NATAPP 這兩個軟件都有免費通道和付費通道,免費的通道不穩(wěn)定,而且每次開啟地址都會變,但如果只是測試可以湊合著用。

    百度搜索NATAPP官網,進去注冊領取免費的隧道,然后配置。

點擊下載客戶端,下載對應系統(tǒng)的natapp.exe文件,然后在natapp.exe文件同目錄下創(chuàng)建config.ini文件,編輯文件內容

[default]

authtoken=                      #對應一條隧道的authtoken

clienttoken=                    #對應客戶端的clienttoken,將會忽略authtoken,若無請留空,

log=none                        #log 日志文件,可指定本地文件, none=不做記錄,stdout=直接屏幕輸出 ,默認為none

loglevel=ERROR                  #日志等級 DEBUG, INFO, WARNING, ERROR 默認為 DEBUG

http_proxy=                     #代理設置 如 http://10.123.10.10:3128 非代理上網用戶請務必留空

只需要在authtoken= 后面填上你注冊的隧道的authtoken碼保存,之后直接打開natapp.exe即可完成內網穿透。

    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多
    喜歡該文的人也喜歡 更多
    熱門閱讀 換一換

    久热这里只有精品九九| 欧美日韩国产精品第五页| 日韩欧美一区二区黄色| 一区二区不卡免费观看免费| 欧美黑人精品一区二区在线| 98精品永久免费视频| 在线观看国产成人av天堂野外| 少妇在线一区二区三区| 99视频精品免费视频播放 | 亚洲欧美日韩中文字幕二欧美| 国产精品一区二区三区黄色片| 国产免费成人激情视频| 99久久免费中文字幕| 中文字幕精品一区二区三| 一区二区三区亚洲国产| 国产又粗又长又大高潮视频| 偷自拍亚洲欧美一区二页| 福利视频一区二区三区| 久久亚洲精品中文字幕| 99久久成人精品国产免费| 厕所偷拍一区二区三区视频| 日本人妻丰满熟妇久久| 欧美日韩乱码一区二区三区| 国产一级内片内射免费看 | 99久久精品免费看国产高清| 99久久人妻中文字幕| 九九热九九热九九热九九热 | 国产欧美日韩精品一区二区| 欧美国产在线观看精品| 日韩欧美精品一区二区三区| 国产欧美日韩精品一区二区| 亚洲精品黄色片中文字幕| 伊人欧美一区二区三区| 熟女高潮一区二区三区| 国产麻豆视频一二三区| 国产性色精品福利在线观看| 福利一区二区视频在线| 久久福利视频视频一区二区| 人妻内射精品一区二区| 国产精品欧美激情在线| 1024你懂的在线视频|