1. 添加统一支付接口

2. 抽离Service接口公共方法
3. 添加部分支付业务逻辑,待完善
This commit is contained in:
Dftre 2025-03-10 00:09:59 +08:00
parent 8b777e1913
commit b4cdbf119b
12 changed files with 198 additions and 214 deletions

View File

@ -7,6 +7,7 @@ pay:
terminalKey: "terminalKey"
vendorSn: "vendorSn"
vendorKey: "vendorKey"
publicKey: classpath:pay/sqb/sqb_public_key.pem
alipay:
enabled: false
appId: appid

View File

@ -1,58 +0,0 @@
package com.ruoyi.pay.alipay.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.pay.alipay.service.IAliPayService;
import com.ruoyi.pay.domain.PayOrder;
import com.ruoyi.pay.service.IPayOrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* @author zlh
*/
@RestController
@RequestMapping("/pay/alipay")
@ConditionalOnProperty(prefix = "pay.alipay", name = "enabled", havingValue = "true")
@Tag(name = "【支付宝】管理")
public class AliPayController extends BaseController {
@Autowired
private IAliPayService aliPayService;
@Autowired
private IPayOrderService payOrderService;
@Anonymous
@Operation(summary = "支付宝支付")
@Parameters({
@Parameter(name = "orderId", description = "订单号", required = true)
})
@GetMapping("/url/{orderNumber}")
public AjaxResult pay(@PathVariable(name = "orderNumber") String orderNumber) {
PayOrder aliPay = payOrderService.selectPayOrderByOrderNumber(orderNumber);
return success(aliPayService.payUrl(aliPay));
}
@Anonymous
@Operation(summary = "支付宝支付回调")
@PostMapping("/notify")
public AjaxResult notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
aliPayService.notify(request, response);
return success("success");
}
}

View File

@ -16,7 +16,7 @@ import com.ruoyi.pay.service.IPayOrderService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@Service
@Service("alipayPayService")
public class AliPayService implements IAliPayService {
public void callback(Map<String, String> params) {
}
@ -39,7 +39,7 @@ public class AliPayService implements IAliPayService {
}
@Override
public void notify(HttpServletRequest request, HttpServletResponse response) {
public String notify(HttpServletRequest request, HttpServletResponse response) {
if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
Map<String, String> params = new HashMap<>();
@ -52,9 +52,23 @@ public class AliPayService implements IAliPayService {
if (Factory.Payment.Common().verifyNotify(params)) {
payOrderService.updateStatus(orderNumber, "已支付");
}
return "success";
} catch (Exception e) {
e.printStackTrace();
}
}
return "fail";
}
@Override
public String query(PayOrder payOrder) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'query'");
}
@Override
public String refund(PayOrder payOrder) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'refund'");
}
}

View File

@ -8,5 +8,9 @@ import jakarta.servlet.http.HttpServletResponse;
public interface PayService {
String payUrl(PayOrder payOrder);
void notify(HttpServletRequest servletRequest, HttpServletResponse response);
String notify(HttpServletRequest servletRequest, HttpServletResponse response);
String query(PayOrder payOrder);
String refund(PayOrder payOrder);
}

View File

@ -1,7 +1,15 @@
package com.ruoyi.pay.sqb.config;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
@Component
@ -34,6 +42,28 @@ public class SqbConfig {
@Value("${pay.sqb.proxy}")
private String proxyPath;
@Value("${pay.sqb.publicKey}")
private String publicKey;
@Autowired
private ApplicationContext applicationContext;
public String getPublicKey() throws Exception {
if (publicKey.startsWith("classpath")) {
Resource resource = applicationContext.getResource(publicKey);
InputStream inputStream = resource.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String alipayPublicKeyValue = bufferedReader.lines().collect(Collectors.joining(System.lineSeparator()));
bufferedReader.close();
publicKey = alipayPublicKeyValue;
}
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
public String getDefaultNotifyUrl() {
return defaultNotifyUrl;
}

View File

@ -1,84 +0,0 @@
package com.ruoyi.pay.sqb.controller;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.pay.domain.PayOrder;
import com.ruoyi.pay.service.IPayOrderService;
import com.ruoyi.pay.sqb.service.ISqbPayService;
import com.ruoyi.pay.sqb.service.Impl.SQBServiceImpl;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
@Tag(name = "sqb支付")
@RestController
@RequestMapping("/pay/sqb")
@ConditionalOnProperty(prefix = "pay.sqb", name = "enabled", havingValue = "true")
public class SQBController extends BaseController {
@Autowired
private SQBServiceImpl sqbServiceImpl;
@Autowired
private IPayOrderService payOrderServicer;
@Autowired(required = false)
private ISqbPayService sqbPayService;
@Operation(summary = "获取支付url")
@Parameters(value = {
@Parameter(name = "orderNumber", description = "订单号", required = true)
})
@PostMapping("/url/{orderNumber}")
@Anonymous
public R<String> url(@PathVariable(name = "orderNumber") String orderNumber) throws Exception {
PayOrder payOrder = payOrderServicer.selectPayOrderByOrderNumber(orderNumber);
String url = sqbServiceImpl.payUrl(payOrder);
return R.ok(url);
}
@Operation(summary = "查询支付状态")
@Parameters(value = {
@Parameter(name = "orderNumber", description = "订单号", required = true)
})
@PostMapping("/query/{orderNumber}")
public AjaxResult query(@PathVariable(name = "orderNumber") String orderNumber) throws Exception {
PayOrder payOrder = payOrderServicer.selectPayOrderByOrderNumber(orderNumber);
return success(sqbServiceImpl.query(payOrder));
}
@PostMapping("/refund")
public AjaxResult refund(@RequestBody PayOrder payOrder) {
String refund = sqbServiceImpl.refund(payOrder);
if (refund == null) {
return error("退款失败");
}
Object parse = JSON.parse(refund);
return success(parse);
}
@PostMapping("/notify")
@Anonymous
@Operation(summary = "支付回调")
public AjaxResult notify(@RequestBody JSONObject jsonObject) throws IOException {
// 验签
// 修改订单状态
// 用户自定义行为
return AjaxResult.success();
}
}

View File

@ -1,17 +1,25 @@
package com.ruoyi.pay.sqb.service.Impl;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.UnrecoverableKeyException;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.springframework.util.StreamUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
@ -27,7 +35,7 @@ import com.ruoyi.pay.sqb.service.ISqbPayService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@Service
@Service("sqbPayService")
@ConditionalOnProperty(prefix = "pay.sqb", name = "enabled", havingValue = "true")
public class SQBServiceImpl implements ISqbPayService {
@Autowired
@ -164,7 +172,7 @@ public class SQBServiceImpl implements ISqbPayService {
* @return
*/
public JSONObject query(PayOrder payOrder) {
public String query(PayOrder payOrder) {
String url = sqbConfig.getApiDomain() + "/upay/v2/query";
JSONObject params = new JSONObject();
try {
@ -179,7 +187,7 @@ public class SQBServiceImpl implements ISqbPayService {
return null;
}
String responseStr = retObj.get("biz_response").toString();
return JSONObject.parseObject(responseStr);
return responseStr;
} catch (Exception e) {
return null;
}
@ -281,10 +289,45 @@ public class SQBServiceImpl implements ISqbPayService {
}
}
public boolean validateSign(String data, String sign) {
try {
// 使用SHA256WithRSA算法
Signature signature = Signature.getInstance("SHA256WithRSA");
// 获取公钥
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey localPublicKey = keyFactory
.generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(sqbConfig.getPublicKey())));
// 初始化验证过程
signature.initVerify(localPublicKey);
signature.update(data.getBytes());
// 解码签名
byte[] bytesSign = Base64.getDecoder().decode(sign);
// 验证签名
return signature.verify(bytesSign);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public void notify(HttpServletRequest servletRequest, HttpServletResponse response) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'notify'");
public String notify(HttpServletRequest request, HttpServletResponse response) {
try {
String requestBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
JSONObject jsonObject = JSONObject.parseObject(requestBody);
String sign = request.getHeader("Authorization");
if (!validateSign(requestBody, sign)) {
throw new ServiceException("收钱吧支付回调验签失败");
}
System.out.println(jsonObject);
return "success";
} catch (IOException e) {
e.printStackTrace();
return "fail";
}
}
}

View File

@ -0,0 +1,76 @@
package com.ruoyi.pay.controller;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.pay.domain.PayOrder;
import com.ruoyi.pay.service.IPayOrderService;
import com.ruoyi.pay.service.PayService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@RequestMapping("/pay/<channel>")
public class PayController extends BaseController {
@Autowired
private Map<String, PayService> payServiceMap; // alipay wechat sqb
@Autowired
private IPayOrderService payOrderService;
@Operation(summary = "微信支付")
@Parameters({
@Parameter(name = "orderNumber", description = "订单号", required = true)
})
@GetMapping("/url/{orderNumber}")
public AjaxResult url(@PathVariable String channel, @PathVariable String orderNumber) throws Exception {
PayService payService = payServiceMap.get(channel + "PayService");
PayOrder payOrder = payOrderService.selectPayOrderByOrderNumber(orderNumber);
return success(payService.payUrl(payOrder));
}
@Anonymous
@Operation(summary = "微信支付查询订单")
@PostMapping("/notify")
public String notify(@PathVariable String channel, HttpServletRequest request, HttpServletResponse response)
throws Exception {
PayService payService = payServiceMap.get(channel + "PayService");
return payService.notify(request, response);
}
@Operation(summary = "查询支付状态")
@Parameters(value = {
@Parameter(name = "orderNumber", description = "订单号", required = true)
})
@PostMapping("/query/{orderNumber}")
public AjaxResult query(@PathVariable String channel, @PathVariable(name = "orderNumber") String orderNumber)
throws Exception {
PayService payService = payServiceMap.get(channel + "PayService");
PayOrder payOrder = payOrderService.selectPayOrderByOrderNumber(orderNumber);
return success(payService.query(payOrder));
}
@PostMapping("/refund")
public AjaxResult refund(@PathVariable String channel, @RequestBody PayOrder payOrder) {
PayService payService = payServiceMap.get(channel + "PayService");
String refund = payService.refund(payOrder);
if (refund == null) {
return error("退款失败");
}
Object parse = JSON.parse(refund);
return success(parse);
}
}

View File

@ -1,56 +0,0 @@
package com.ruoyi.pay.wx.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.pay.domain.PayOrder;
import com.ruoyi.pay.service.IPayOrderService;
import com.ruoyi.pay.wx.service.IWxPayService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* @author zlh
*/
@RestController
@ConditionalOnProperty(prefix = "pay.wechat", name = "enabled", havingValue = "true")
@RequestMapping("/pay/wechat")
public class WxPayController extends BaseController {
@Autowired
private IWxPayService wxPayService;
@Autowired
private IPayOrderService payOrderService;
@Operation(summary = "微信支付")
@Parameters({
@Parameter(name = "orderNumber", description = "订单号", required = true)
})
@GetMapping("/url/{orderNumber}")
public AjaxResult url(@PathVariable(name = "orderNumber") String orderNumber) throws Exception {
PayOrder payOrder = payOrderService.selectPayOrderByOrderNumber(orderNumber);
return success(wxPayService.payUrl(payOrder));
}
@Anonymous
@Operation(summary = "微信支付查询订单")
@PostMapping("/notify")
public AjaxResult notify(HttpServletRequest request, HttpServletResponse response) throws Exception {
wxPayService.notify(request, response);
return success();
}
}

View File

@ -21,7 +21,7 @@ import com.wechat.pay.java.service.wexinpayscoreparking.model.Transaction;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@Service
@Service("wechatPayService")
public class WxPayService implements IWxPayService {
@Autowired
@ -56,13 +56,13 @@ public class WxPayService implements IWxPayService {
}
@Override
public void notify(HttpServletRequest servletRequest, HttpServletResponse response) {
String timeStamp = servletRequest.getHeader("Wechatpay-Timestamp");
String nonce = servletRequest.getHeader("Wechatpay-Nonce");
String signature = servletRequest.getHeader("Wechatpay-Signature");
String certSn = servletRequest.getHeader("Wechatpay-Serial");
public String notify(HttpServletRequest request, HttpServletResponse response) {
String timeStamp = request.getHeader("Wechatpay-Timestamp");
String nonce = request.getHeader("Wechatpay-Nonce");
String signature = request.getHeader("Wechatpay-Signature");
String certSn = request.getHeader("Wechatpay-Serial");
try {
String requestBody = StreamUtils.copyToString(servletRequest.getInputStream(), StandardCharsets.UTF_8);
String requestBody = StreamUtils.copyToString(request.getInputStream(), StandardCharsets.UTF_8);
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(certSn)
.nonce(nonce)
@ -77,9 +77,23 @@ public class WxPayService implements IWxPayService {
System.out.println("orderNumber: " + orderNumber);
System.out.println("otherOrderNumber: " + otherOrderNumber);
System.out.println("orderState: " + orderState);
return "success";
} catch (Exception e) {
e.printStackTrace();
return "fail";
}
}
@Override
public String query(PayOrder payOrder) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'query'");
}
@Override
public String refund(PayOrder payOrder) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'refund'");
}
}