UniApp?+?SpringBoot?實現(xiàn)微信支付和退款功能
開發(fā)準備
- 一臺用于支付的測試機,必須得是一個安卓機因為需要打支付基座才能使用。
- 用于編寫的后端框架接口的 IDE (IDEA 或者 Eclipse 都可以)
- HBuilder X 用來編輯 UniApp 項目的編輯器和編譯器
- 基本的 SpringBoot 的腳手架,可以去 https://start.spring.io/ 或者 IDEA 自帶的快速生成腳手架插件。
- Jdk 11
微信支付開發(fā)
我這里省略了申請等步驟。如果沒有申請過企業(yè)支付的可以去官網(wǎng)申請 https://pay.weixin.qq.com/static/applyment_guide/applyment_detail_app.shtml 。安卓測試必須要打成基座,或者是正式APP應(yīng)用。
后端部分
在 SpringBoot 中添加以下坐標
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 微信支付坐標 start-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-pay</artifactId>
<version>4.2.5.B</version>
</dependency>
<!-- 退款用 -->
<dependency>
<groupId>org.jodd</groupId>
<artifactId>jodd-http</artifactId>
<version>6.0.8</version>
</dependency>
<!-- 微信支付坐標 end-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>在 resources 目錄下添加 application.yml 我們不去用默認的 application.properties 文件,畢竟 yml 更好看點。并在 yml 中添加以下內(nèi)容
# 服務(wù)啟動端口 server: port: 8080 # 微信支付 wxpay: appId: 開放平臺的AppID mchId: 商戶號 mchKey: 商戶密鑰 # p12證書文件的絕對路徑或者以classpath:開頭的類路徑. keyPath: classpath:/wxpay_cert/apiclient_cert.p12 # apiclient_key.pem證書文件的絕對路徑或者以classpath:開頭的類路徑. privateKeyPath: classpath:/wxpay_cert/apiclient_key.pem privateCertPath: classpath:/wxpay_cert/apiclient_cert.pem notifyUrl: https://4789j06630.wocp.fun/wechat/pay/notify refundNotifyUrl: https://4789j06630.wocp.fun/wechat/pay/refund_notify
創(chuàng)建一個 WechatPayConfig.java 使用上面的 ****wxpay
@Data
@ConfigurationProperties(prefix = "wxpay")
public class WechatPayConfig {
private String appId;
private String mchId;
private String mchKey;
private String keyPath;
private String privateKeyPath;
private String privateCertPath;
private String notifyUrl;
private String refundNotifyUrl;
}創(chuàng)建一個 BizWechatPayService.java
package com.runbrick.paytest.util.wxpay;
import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.constant.WxPayConstants;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;
import java.net.InetAddress;
/**
* 微信支付
*/
@Service
@ConditionalOnClass(WxPayService.class)
@EnableConfigurationProperties(WechatPayConfig.class)
@AllArgsConstructor
public class BizWechatPayService {
private WechatPayConfig wechatPayConfig;
public WxPayService wxPayService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(wechatPayConfig.getAppId());
payConfig.setMchId(wechatPayConfig.getMchId());
payConfig.setMchKey(wechatPayConfig.getMchKey());
payConfig.setKeyPath(wechatPayConfig.getKeyPath());
payConfig.setPrivateKeyPath(wechatPayConfig.getPrivateKeyPath());
payConfig.setPrivateCertPath(wechatPayConfig.getPrivateCertPath());
// 可以指定是否使用沙箱環(huán)境
payConfig.setUseSandboxEnv(false);
payConfig.setSignType("MD5");
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
/**
* 創(chuàng)建微信訂單給APP
*
* @param productTitle 商品標題
* @param outTradeNo 訂單號
* @param totalFee 總價
* @return
*/
public Object createOrder(String productTitle, String outTradeNo, Integer totalFee) {
try {
WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
// 支付描述
request.setBody(productTitle);
// 訂單號
request.setOutTradeNo(outTradeNo);
// 請按照分填寫
request.setTotalFee(totalFee);
// 回調(diào)鏈接
request.setNotifyUrl(wechatPayConfig.getNotifyUrl());
// 終端IP.
request.setSpbillCreateIp(InetAddress.getLocalHost().getHostAddress());
// 設(shè)置類型為APP
request.setTradeType(WxPayConstants.TradeType.APP);
// 一定要用 createOrder 不然得自己做二次校驗
Object order = wxPayService().createOrder(request);
return order;
} catch (Exception e) {
return null;
}
}
/**
* 退款
*
* @param tradeNo
* @param totalFee
* @return
*/
public WxPayRefundResult refund(String tradeNo, Integer totalFee) {
WxPayRefundRequest wxPayRefundRequest = new WxPayRefundRequest();
wxPayRefundRequest.setTransactionId(tradeNo);
wxPayRefundRequest.setOutRefundNo(String.valueOf(System.currentTimeMillis()));
wxPayRefundRequest.setTotalFee(totalFee);
wxPayRefundRequest.setRefundFee(totalFee);
wxPayRefundRequest.setNotifyUrl(wechatPayConfig.getRefundNotifyUrl());
try {
WxPayRefundResult refund = wxPayService().refundV2(wxPayRefundRequest);
if (refund.getReturnCode().equals("SUCCESS") && refund.getResultCode().equals("SUCCESS")) {
return refund;
}
} catch (WxPayException e) {
e.printStackTrace();
}
return null;
}
}創(chuàng)建一個 WechatController.java 來實現(xiàn)接口給前端調(diào)用時使用
package com.runbrick.paytest.controller;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.runbrick.paytest.util.wxpay.BizWechatPayService;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/wechat/pay")
@AllArgsConstructor
public class WechatController {
BizWechatPayService wechatPayService;
private static Logger logger = LoggerFactory.getLogger(WechatController.class);
/**
* 創(chuàng)建微信訂單給APP
*
* @return
*/
@RequestMapping(value = "/unified/request", method = RequestMethod.GET)
public Object appPayUnifiedRequest() {
// totalFee 必須要以分為單位
Object createOrderResult = wechatPayService.createOrder("測試支付", String.valueOf(System.currentTimeMillis()), 1);
logger.info("統(tǒng)一下單的生成的參數(shù):{}", createOrderResult);
return createOrderResult;
}
@RequestMapping(method = RequestMethod.POST, value = "notify")
public String notify(@RequestBody String xmlData) {
try {
WxPayOrderNotifyResult result = wechatPayService.wxPayService().parseOrderNotifyResult(xmlData);
// 支付返回信息
if ("SUCCESS".equals(result.getReturnCode())) {
// 可以實現(xiàn)自己的邏輯
logger.info("來自微信支付的回調(diào):{}", result);
}
return WxPayNotifyResponse.success("成功");
} catch (WxPayException e) {
logger.error(e.getMessage());
return WxPayNotifyResponse.fail("失敗");
}
}
/**
* 退款
*
* @param transaction_id
*/
@RequestMapping(method = RequestMethod.POST, value = "refund")
public void refund(String transaction_id) {
// totalFee 必須要以分為單位,退款的價格可以這里只做的全部退款
WxPayRefundResult refund = wechatPayService.refund(transaction_id, 1);
// 實現(xiàn)自己的邏輯
logger.info("退款本地回調(diào):{}", refund);
}
/**
* 退款回調(diào)
*
* @param xmlData
* @return
*/
@RequestMapping(method = RequestMethod.POST, value = "refund_notify")
public String refundNotify(@RequestBody String xmlData) {
// 實現(xiàn)自己的邏輯
logger.info("退款遠程回調(diào):{}", xmlData);
// 必須要返回 SUCCESS 不過有 WxPayNotifyResponse 給整合成了 xml了
return WxPayNotifyResponse.success("成功");
}
}上面的 controller 寫了兩個接口一個用來 app端的調(diào)用,一個給支付用來回調(diào)?;卣{(diào)接口的地址要放到剛才配置中的 notifyUrl 屬性里。還有一個是微信的退款接口。
- 由于支付寶回調(diào)要使用線上的地址作為回調(diào)地址,這里我推薦兩個解決辦法
- 使用一臺服務(wù)器+備案的域名搭建上面的后臺地址
- 使用 花生殼 來實現(xiàn)本地內(nèi)網(wǎng)穿透
我使用的是 花生殼 作為本次的開發(fā)環(huán)境,啟動 springboot 的服務(wù),配置好花生殼。后臺部分到目前為止已經(jīng)結(jié)束了。
前端部分
創(chuàng)建部分和我寫的支付寶那個一樣,如果不知道可以去看一下。所以跳過創(chuàng)建部分了,直接來到了代碼實現(xiàn)。要在 manifest.json 勾選微信支付支持

創(chuàng)建前端支付代碼 index.vue
<template>
<view class="content">
<view class="text-area">
<text class="title">{{title}}</text>
</view>
<button type="default" @click="goPay()">點我前去支付</button>
</view>
</template>
<script>
export default {
data() {
return {
title: '跟我去支付'
}
},
onLoad() {
},
methods: {
goPay() {
uni.request({
url: "https://4789j06630.wocp.fun/wechat/pay/unified/request",
success(res) {
let obj = {
appid: res.data.appId,
noncestr: res.data.nonceStr,
package: res.data.packageValue,
partnerid: res.data.partnerId,
prepayid: res.data.prepayId,
timestamp: parseInt(res.data.timeStamp),
sign: res.data.sign,
};
uni.requestPayment({
provider: "wxpay",
orderInfo: obj,
success(res) {
uni.showModal({
content: "支付成功",
showCancel: false
})
},
fail(e) {
uni.showModal({
content: "支付失敗,原因為: " + e.errMsg,
showCancel: false
})
},
complete() {
console.log("啥也沒干");
}
});
}
})
}
}
}
</script>
<style>
page {
background-color: #ff5500;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>

點擊按鈕就可以前往微信支付看下后臺的生成的組合參數(shù)


跳轉(zhuǎn)微信支付之后會跳回這里,提示支付成功。查看一下后臺回調(diào)

之后的業(yè)務(wù)按照支付邏輯開發(fā)就可以,簡單的支付已經(jīng)完成。在按照剛才給的回調(diào)參數(shù)做個退款操作
我們使用 apipost 一個很強大的工具,被同事安利的。那就正好拿他測測退款借口,就不寫代碼了。

此時如果沒有任何錯誤,后臺控制臺會返回退款本地和遠程回調(diào)信息

此時微信也收到退款信息了。
整套支付流程都上傳到 github 了可以查看 github的源碼 https://github.com/runbrick/pay_spring
到此這篇關(guān)于UniApp + SpringBoot 實現(xiàn)微信支付和退款的文章就介紹到這了,更多相關(guān)SpringBoot 微信支付和退款內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
解析Java的Spring框架的基本結(jié)構(gòu)
這篇文章主要介紹了Java的Spring框架的基本結(jié)構(gòu),作者從Spring的設(shè)計角度觸發(fā)解析其基礎(chǔ)的架構(gòu)內(nèi)容,需要的朋友可以參考下2016-03-03
SpringFramework中的數(shù)據(jù)校驗方式
這篇文章主要介紹了SpringFramework中的數(shù)據(jù)校驗方式,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧2024-12-12
SpringBoot預(yù)加載與懶加載實現(xiàn)方法超詳細講解
Spring一直被詬病啟動時間慢,可Spring/SpringBoot是輕量級的框架。因為當(dāng)Spring項目越來越大的時候,在啟動時加載和初始化Bean就會變得越來越慢,很多時候我們在啟動時并不需要加載全部的Bean,在調(diào)用時再加載就行,那這就需要預(yù)加載與懶加載的功能了2022-11-11
spring-boot-maven-plugin引入出現(xiàn)爆紅(已解決)
這篇文章主要介紹了spring-boot-maven-plugin引入出現(xiàn)爆紅(已解決),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
Spring Security 自定義短信登錄認證的實現(xiàn)
這篇文章主要介紹了Spring Security 自定義短信登錄認證的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
在Java中將List轉(zhuǎn)換為String輸出過程解析
這篇文章主要介紹了在Java中將List轉(zhuǎn)換為String輸出過程解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-09-09

