@@ -13,6 +13,7 @@ import org.mybatis.spring.annotation.MapperScan;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||
@@ -46,6 +47,8 @@ public class UpmsAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "ballcat.login.captcha", name = "enabled", havingValue = "true",
|
||||
matchIfMissing = true)
|
||||
public FilterRegistrationBean<LoginCaptchaFilter> filterRegistrationBean(ObjectMapper objectMapper,
|
||||
CaptchaService captchaService) {
|
||||
FilterRegistrationBean<LoginCaptchaFilter> bean = new FilterRegistrationBean<>();
|
||||
|
||||
@@ -59,7 +59,7 @@ public class SysUserController {
|
||||
/**
|
||||
* TODO 封装为实体对象,方便归档系统参数
|
||||
*/
|
||||
@Value("${password.secret-key}")
|
||||
@Value("${ballcat.password.secret-key}")
|
||||
private String passwordSecretKey;
|
||||
|
||||
/**
|
||||
|
||||
@@ -37,7 +37,7 @@ public class LoginPasswordDecoderFilter extends OncePerRequestFilter {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Value("${password.secret-key}")
|
||||
@Value("${ballcat.password.secret-key}")
|
||||
private String secretKey;
|
||||
|
||||
private static final String PASSWORD = "password";
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"properties": [
|
||||
{
|
||||
"name": "ballcat.login.captcha.enabled",
|
||||
"type": "java.lang.Boolean",
|
||||
"description": "是否开启登录验证码.",
|
||||
"defaultValue": true
|
||||
},
|
||||
{
|
||||
"name": "ballcat.password.secret-key",
|
||||
"type": "java.lang.String",
|
||||
"description": "密码加解密密钥."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import com.alipay.api.DefaultAlipayClient;
|
||||
import com.alipay.api.domain.AlipayTradePayModel;
|
||||
import com.alipay.api.domain.AlipayTradeQueryModel;
|
||||
import com.alipay.api.domain.AlipayTradeRefundModel;
|
||||
import com.alipay.api.internal.util.AlipaySignature;
|
||||
import com.alipay.api.request.AlipayTradeAppPayRequest;
|
||||
import com.alipay.api.request.AlipayTradePayRequest;
|
||||
import com.alipay.api.request.AlipayTradeQueryRequest;
|
||||
@@ -15,13 +16,13 @@ import com.alipay.api.request.AlipayTradeRefundRequest;
|
||||
import com.alipay.api.request.AlipayTradeWapPayRequest;
|
||||
import com.alipay.api.response.AlipayTradeAppPayResponse;
|
||||
import com.alipay.api.response.AlipayTradePayResponse;
|
||||
import com.alipay.api.response.AlipayTradeQueryResponse;
|
||||
import com.alipay.api.response.AlipayTradeRefundResponse;
|
||||
import com.alipay.api.response.AlipayTradeWapPayResponse;
|
||||
import com.hccake.starte.pay.ali.domain.AliPayQuery;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* api文档: https://opendocs.alipay.com/apis.
|
||||
@@ -286,7 +287,7 @@ public class AliPay {
|
||||
* @return com.alipay.api.response.AlipayTradeQueryResponse
|
||||
* @author lingting 2021-01-25 11:12
|
||||
*/
|
||||
public AlipayTradeQueryResponse query(String sn) throws AlipayApiException {
|
||||
public AliPayQuery query(String sn) throws AlipayApiException {
|
||||
return query(sn, null);
|
||||
}
|
||||
|
||||
@@ -297,7 +298,7 @@ public class AliPay {
|
||||
* @return com.alipay.api.response.AlipayTradeQueryResponse
|
||||
* @author lingting 2021-01-25 11:12
|
||||
*/
|
||||
public AlipayTradeQueryResponse query(String sn, String tradeNo) throws AlipayApiException {
|
||||
public AliPayQuery query(String sn, String tradeNo) throws AlipayApiException {
|
||||
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
|
||||
model.setOutTradeNo(sn);
|
||||
model.setTradeNo(tradeNo);
|
||||
@@ -309,10 +310,10 @@ public class AliPay {
|
||||
* @return com.alipay.api.response.AlipayTradeQueryResponse
|
||||
* @author lingting 2021-01-25 11:12
|
||||
*/
|
||||
public AlipayTradeQueryResponse query(AlipayTradeQueryModel model) throws AlipayApiException {
|
||||
public AliPayQuery query(AlipayTradeQueryModel model) throws AlipayApiException {
|
||||
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
|
||||
request.setBizModel(model);
|
||||
return client.execute(request);
|
||||
return AliPayQuery.of(client.execute(request));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,6 +354,30 @@ public class AliPay {
|
||||
return client.execute(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* v1 版本验签
|
||||
* @param map 所有参数
|
||||
* @return boolean
|
||||
* @author lingting 2021-01-26 14:46
|
||||
*/
|
||||
public boolean checkSignV1(Map<String, String> map) throws AlipayApiException {
|
||||
// 验签需要先移除 fund_bill_list 参数值中的 " 否则会导致正确的签名验签失败
|
||||
map.put("fund_bill_list", map.get("fund_bill_list").replaceAll(""","\""));
|
||||
return AlipaySignature.rsaCheckV1(map, alipayPublicKey, charset, signType);
|
||||
}
|
||||
|
||||
/**
|
||||
* v2 版本验签
|
||||
* @param map 所有参数
|
||||
* @return boolean
|
||||
* @author lingting 2021-01-26 14:46
|
||||
*/
|
||||
public boolean checkSignV2(Map<String, String> map) throws AlipayApiException {
|
||||
// 验签需要先移除 fund_bill_list 参数值中的 " 否则会导致正确的签名验签失败
|
||||
map.put("fund_bill_list", map.get("fund_bill_list").replaceAll(""","\""));
|
||||
return AlipaySignature.rsaCheckV2(map, alipayPublicKey, charset, signType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额单位转换, 元 转为 分
|
||||
* @param amount 支付金额, 单位 元
|
||||
|
||||
@@ -22,4 +22,9 @@ public class AliPayConstant {
|
||||
*/
|
||||
public static final String SERVER_URL_DEV = "https://openapi.alipaydev.com/gateway.do";
|
||||
|
||||
/**
|
||||
* 查询支付成功返回code
|
||||
*/
|
||||
public static final String CODE_SUCCESS = "10000";
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.hccake.starte.pay.ali.domain;
|
||||
|
||||
import static com.hccake.ballcat.common.core.util.JacksonUtils.toJson;
|
||||
import static com.hccake.ballcat.common.core.util.JacksonUtils.toObj;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.hccake.starte.pay.ali.enums.TradeStatus;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author lingting 2021/1/26 13:31
|
||||
*/
|
||||
@NoArgsConstructor
|
||||
@Data
|
||||
public class AliPayCallback {
|
||||
|
||||
/**
|
||||
* 解析回调参数
|
||||
* @param callbackParams 所有回调参数
|
||||
* @return com.hccake.starte.pay.ali.domain.AliPayCallback
|
||||
* @author lingting 2021-01-26 14:39
|
||||
*/
|
||||
public static AliPayCallback of(Map<String, String> callbackParams) {
|
||||
Map<String, Object> map = new HashMap<>(callbackParams);
|
||||
String fundBillListStr = callbackParams.get("fund_bill_list").replaceAll(""", "\"");
|
||||
map.put("fund_bill_list", toObj(fundBillListStr, List.class));
|
||||
// 覆盖原值
|
||||
callbackParams.put("fund_bill_list", fundBillListStr);
|
||||
return toObj(toJson(map), AliPayCallback.class);
|
||||
}
|
||||
|
||||
@JsonProperty("gmt_create")
|
||||
private String gmtCreate;
|
||||
|
||||
@JsonProperty("charset")
|
||||
private String charset;
|
||||
|
||||
@JsonProperty("seller_email")
|
||||
private String sellerEmail;
|
||||
|
||||
@JsonProperty("subject")
|
||||
private String subject;
|
||||
|
||||
@JsonProperty("sign")
|
||||
private String sign;
|
||||
|
||||
@JsonProperty("buyer_id")
|
||||
private String buyerId;
|
||||
|
||||
@JsonProperty("invoice_amount")
|
||||
private BigDecimal invoiceAmount;
|
||||
|
||||
@JsonProperty("notify_id")
|
||||
private String notifyId;
|
||||
|
||||
@JsonProperty("fund_bill_list")
|
||||
private List<FundBill> fundBillList;
|
||||
|
||||
@JsonProperty("notify_type")
|
||||
private String notifyType;
|
||||
|
||||
@JsonProperty("trade_status")
|
||||
private TradeStatus tradeStatus;
|
||||
|
||||
@JsonProperty("receipt_amount")
|
||||
private BigDecimal receiptAmount;
|
||||
|
||||
@JsonProperty("app_id")
|
||||
private String appId;
|
||||
|
||||
@JsonProperty("buyer_pay_amount")
|
||||
private BigDecimal buyerPayAmount;
|
||||
|
||||
@JsonProperty("sign_type")
|
||||
private String signType;
|
||||
|
||||
@JsonProperty("seller_id")
|
||||
private String sellerId;
|
||||
|
||||
@JsonProperty("gmt_payment")
|
||||
private String gmtPayment;
|
||||
|
||||
@JsonProperty("notify_time")
|
||||
private String notifyTime;
|
||||
|
||||
@JsonProperty("version")
|
||||
private String version;
|
||||
|
||||
@JsonProperty("out_trade_no")
|
||||
private String outTradeNo;
|
||||
|
||||
@JsonProperty("total_amount")
|
||||
private BigDecimal totalAmount;
|
||||
|
||||
@JsonProperty("trade_no")
|
||||
private String tradeNo;
|
||||
|
||||
@JsonProperty("auth_app_id")
|
||||
private String authAppId;
|
||||
|
||||
@JsonProperty("buyer_logon_id")
|
||||
private String buyerLogonId;
|
||||
|
||||
@JsonProperty("point_amount")
|
||||
private BigDecimal pointAmount;
|
||||
|
||||
@Data
|
||||
public static class FundBill {
|
||||
|
||||
private BigDecimal amount;
|
||||
|
||||
private String fundChannel;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.hccake.starte.pay.ali.domain;
|
||||
|
||||
import static com.hccake.starte.pay.ali.constants.AliPayConstant.CODE_SUCCESS;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alipay.api.response.AlipayTradeQueryResponse;
|
||||
import com.hccake.starte.pay.ali.enums.TradeStatus;
|
||||
import java.math.BigDecimal;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 简化查询结果
|
||||
*
|
||||
* @author lingting 2021/1/26 10:34
|
||||
*/
|
||||
@Getter
|
||||
@ToString
|
||||
@Accessors(chain = true)
|
||||
@Setter(AccessLevel.PRIVATE)
|
||||
public class AliPayQuery {
|
||||
|
||||
public static AliPayQuery of(AlipayTradeQueryResponse raw) {
|
||||
AliPayQuery query = new AliPayQuery();
|
||||
if (raw == null) {
|
||||
return query;
|
||||
}
|
||||
// 状态处理
|
||||
if (CODE_SUCCESS.equals(raw.getCode())) {
|
||||
// 成功
|
||||
query.setStatus(TradeStatus.of(raw.getTradeStatus()));
|
||||
}
|
||||
// 异常
|
||||
else {
|
||||
query.setStatus(TradeStatus.ERROR);
|
||||
}
|
||||
|
||||
// 金额
|
||||
if (StrUtil.isBlank(raw.getTotalAmount())) {
|
||||
query.setAmount(BigDecimal.ZERO);
|
||||
}
|
||||
else {
|
||||
query.setAmount(new BigDecimal(raw.getTotalAmount()));
|
||||
}
|
||||
|
||||
// 信息
|
||||
query.setCode(raw.getCode()).setMsg(raw.getMsg()).setSubCode(raw.getSubCode()).setSubMsg(raw.getSubMsg());
|
||||
|
||||
// 基础数据
|
||||
return query.setTradeNo(raw.getTradeNo()).setSn(raw.getOutTradeNo()).setId(raw.getBuyerLogonId())
|
||||
.setUserId(raw.getBuyerUserId()).setUserName(raw.getBuyerUserName())
|
||||
.setUserType(raw.getBuyerUserType());
|
||||
}
|
||||
|
||||
/**
|
||||
* 原始数据
|
||||
*/
|
||||
private AlipayTradeQueryResponse raw;
|
||||
|
||||
/**
|
||||
* 订单状态
|
||||
*/
|
||||
private TradeStatus status;
|
||||
|
||||
private String code;
|
||||
|
||||
private String msg;
|
||||
|
||||
private String subCode;
|
||||
|
||||
private String subMsg;
|
||||
|
||||
/**
|
||||
* 金额(单位: 元)
|
||||
*/
|
||||
private BigDecimal amount;
|
||||
|
||||
/**
|
||||
* 平台订单号
|
||||
*/
|
||||
private String sn;
|
||||
|
||||
/**
|
||||
* 支付宝订单号
|
||||
*/
|
||||
private String tradeNo;
|
||||
|
||||
/**
|
||||
* 支付用户支付宝账号信息
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 支付用户id
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
private String userName;
|
||||
|
||||
private String userType;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.hccake.starte.pay.ali.enums;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
||||
/**
|
||||
* 交易状态
|
||||
*/
|
||||
public enum TradeStatus {
|
||||
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
SUCCESS,
|
||||
/**
|
||||
* 未支付
|
||||
*/
|
||||
WAIT,
|
||||
/**
|
||||
* 未付款交易超时关闭,或支付完成后全额退款
|
||||
*/
|
||||
CLOSED,
|
||||
/**
|
||||
* 交易结束,不可退款
|
||||
*/
|
||||
FINISHED,
|
||||
/**
|
||||
* 异常. 具体信息查询 subCode和subMsg
|
||||
*/
|
||||
ERROR,
|
||||
|
||||
;
|
||||
|
||||
@JsonCreator
|
||||
public static TradeStatus of(String status) {
|
||||
switch (status) {
|
||||
case "WAIT_BUYER_PAY":
|
||||
return WAIT;
|
||||
case "TRADE_CLOSED":
|
||||
return CLOSED;
|
||||
case "TRADE_SUCCESS":
|
||||
return SUCCESS;
|
||||
case "TRADE_FINISHED":
|
||||
return FINISHED;
|
||||
default:
|
||||
return ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,6 +14,8 @@ spring:
|
||||
password: 123456
|
||||
|
||||
ballcat:
|
||||
password:
|
||||
secret-key: '==BallCat-Auth=='
|
||||
redis:
|
||||
key-prefix: 'ballcat:'
|
||||
# actuator 加解密密钥
|
||||
@@ -43,6 +45,9 @@ ballcat:
|
||||
aggregator:
|
||||
provider-resources:
|
||||
- { name: ballcat-api, url: http://ballcat-api:9090/v2/api-docs, swagger-version: 2.0 }
|
||||
login:
|
||||
captcha:
|
||||
enabled: true
|
||||
|
||||
security:
|
||||
oauth2:
|
||||
@@ -82,11 +87,6 @@ mybatis-plus:
|
||||
logic-delete-value: "NOW()" # 逻辑已删除值(使用当前时间标识)
|
||||
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
|
||||
|
||||
|
||||
# 密码加解密密钥
|
||||
password:
|
||||
secret-key: '==BallCat-Auth=='
|
||||
|
||||
# 定时任务相关配置
|
||||
xxl:
|
||||
job:
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.hccake</groupId>
|
||||
<artifactId>ballcat-common-conf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.hccake</groupId>
|
||||
<artifactId>ballcat-spring-boot-starter-pay</artifactId>
|
||||
|
||||
@@ -3,12 +3,15 @@ package com.hccake.sample.pay.ali;
|
||||
import cn.hutool.core.lang.Snowflake;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.hccake.starte.pay.ali.AliPay;
|
||||
import com.hccake.starte.pay.ali.domain.AliPayCallback;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
@@ -21,18 +24,27 @@ public class Controller {
|
||||
|
||||
private final AliPay aliPay;
|
||||
|
||||
BigDecimal amount = new BigDecimal("100");
|
||||
|
||||
BigDecimal zero = new BigDecimal("0.01");
|
||||
|
||||
private static final Snowflake snowflake = IdUtil.createSnowflake(1, 1);
|
||||
|
||||
/**
|
||||
* 支付宝支付回调
|
||||
* @param callback 回调参数
|
||||
* @return java.lang.String
|
||||
* @author lingting 2021-01-26 15:18
|
||||
*/
|
||||
@PostMapping
|
||||
public String notice(Map<String, Object> params) {
|
||||
|
||||
public String notice(HttpServletRequest request, @RequestParam Map<String, String> callback) {
|
||||
System.out.println("notice");
|
||||
AliPayCallback of = AliPayCallback.of(callback);
|
||||
return "success";
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public String debug() {
|
||||
String sn = snowflake.nextIdStr();
|
||||
BigDecimal amount = new BigDecimal("100");
|
||||
// System.out.printf(sn);
|
||||
// aliPay.codePay(sn, amount, "280528061260052112", "测试");
|
||||
return "success";
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
spring:
|
||||
application:
|
||||
name: 支付演示
|
||||
|
||||
ballcat:
|
||||
pay:
|
||||
bitcoin:
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.hccake.starter.pay.virtual.VirtualPayAutoConfiguration
|
||||
com.hccake.starter.pay.virtual.VirtualPayAutoConfiguration,\
|
||||
com.hccake.starter.pay.ali.AliPayAutoConfiguration
|
||||
Reference in New Issue
Block a user