[fix]微信充值和提现

This commit is contained in:
wangqx 2025-09-20 18:18:55 +08:00
parent 568cf4a9be
commit 4b518e023c
28 changed files with 445 additions and 207 deletions

View File

@ -1,10 +1,22 @@
package org.dromara.app;
import com.wzj.soopin.transaction.business.IChargeBusiness;
import com.wzj.soopin.transaction.convert.ChargeConvert;
import com.wzj.soopin.transaction.domain.bo.ChargeBO;
import com.wzj.soopin.transaction.domain.bo.PaymentBO;
import com.wzj.soopin.transaction.domain.po.Charge;
import com.wzj.soopin.transaction.domain.po.PayOrder;
import com.wzj.soopin.transaction.enums.PaymentClientEnum;
import com.wzj.soopin.transaction.enums.PaymentMethodEnum;
import com.wzj.soopin.transaction.enums.TransState;
import com.wzj.soopin.transaction.enums.easypay.PayType;
import com.wzj.soopin.transaction.kit.CashierSupport;
import com.wzj.soopin.transaction.kit.dto.PayParam;
import com.wzj.soopin.transaction.kit.params.dto.CashierParam;
import com.wzj.soopin.transaction.service.IChargeService;
import com.wzj.soopin.transaction.service.PayOrderService;
import com.wzj.soopin.transaction.util.SnowFlake;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
@ -16,10 +28,14 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.ResultCode;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Date;
/**
* 买家端,收银台接口
@ -34,11 +50,11 @@ import org.springframework.web.bind.annotation.*;
@RequiredArgsConstructor
public class AppPayController {
private final IChargeBusiness chargeBusiness;
private final CashierSupport cashierSupport;
// @ApiImplicitParams({
// @ApiImplicitParams({
// @ApiImplicitParam(name = "client", value = "客户端类型", paramType = "path", allowableValues = "PC,H5,WECHAT_MP,APP")
// })
@PostMapping(value = "/tradeDetail")
@ -51,19 +67,29 @@ public class AppPayController {
@ApiImplicitParams({
@ApiImplicitParam(name = "paymentMethod", value = "支付方式", paramType = "path", allowableValues = "WECHAT,ALIPAY"),
@ApiImplicitParam(name = "paymentClient", value = "调起方式", paramType = "path", allowableValues = "APP,NATIVE,JSAPI,H5,MP")
@ApiImplicitParam(name = "paymentMethod", value = "支付方式", paramType = "path", allowableValues = "WECHAT,ALIPAY"),
@ApiImplicitParam(name = "paymentClient", value = "调起方式", paramType = "path", allowableValues = "APP,NATIVE,JSAPI,H5,MP")
})
@PostMapping(value = "/pay")
@Operation(summary = "支付")
public R payment(
HttpServletRequest request,
HttpServletResponse response,
@RequestBody @Validated PayParam payParam) {
HttpServletRequest request,
HttpServletResponse response,
@RequestBody @Validated PayParam payParam, @RequestBody ChargeBO chargeBO) {
PaymentMethodEnum paymentMethodEnum = PaymentMethodEnum.valueOf(payParam.getPaymentMethod());
PaymentClientEnum paymentClientEnum = PaymentClientEnum.valueOf(payParam.getPaymentClient());
try {
LoginUser user = LoginHelper.getLoginUser();
if (user == null) {
return R.notLogin();
}
//先生成充值订单
chargeBO.setMemberId(user.getUserId());
chargeBO.setMethod(paymentMethodEnum.getCode());
chargeBusiness.charge(chargeBO);
payParam.setSn(chargeBO.getCode());
return cashierSupport.payment(paymentMethodEnum, paymentClientEnum, request, response, payParam);
} catch (ServiceException se) {
log.info("支付异常", se);

Binary file not shown.

View File

@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEKzCCAxOgAwIBAgIUa6aB2bIZA01veFH1fWG+kxerSP0wDQYJKoZIhvcNAQEL
BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT
FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg
Q0EwHhcNMjMxMTA2MDkwODIwWhcNMjgxMTA0MDkwODIwWjCBhDETMBEGA1UEAwwK
MTY1ODY2NTcxMDEbMBkGA1UECgwS5b6u5L+h5ZWG5oi357O757ufMTAwLgYDVQQL
DCfml6Dnu4jooZfvvIjlpKnmtKXvvInnp5HmioDmnInpmZDlhazlj7gxCzAJBgNV
BAYMAkNOMREwDwYDVQQHDAhTaGVuWmhlbjCCASIwDQYJKoZIhvcNAQEBBQADggEP
ADCCAQoCggEBAPIJ7lvlud4KUXNjQb044xtoBWuF516f8h2mPrtIMkrKb4R9gKbE
OaRFbeGOL5FAcRS0jWYtEoWgRrQ1EVim0Lhlqq5OAAiKtgTphW0ZSiK4dauhzFMI
WByO/KalzzwKWFT0iFHP4UqiWZL1g9Dw9BUiJM3EnutQ7gP9d2wAm8h4Ltoo4Mfm
RA5IqQzlItXrNMEr/iHpAsPEWuQOviVuut2xJHhm7blgWqg0IwtdljvSYGXE77Hv
Cjr1RCVxTC8xtmKc8X5kaKEXH1d9fFH8sovyTF5pOiYZEkhOybtJE626etHbrVhk
Nl0YDS1o9ypNrcYIQe/Um9JuCqxOl6H/Uh0CAwEAAaOBuTCBtjAJBgNVHRMEAjAA
MAsGA1UdDwQEAwID+DCBmwYDVR0fBIGTMIGQMIGNoIGKoIGHhoGEaHR0cDovL2V2
Y2EuaXRydXMuY29tLmNuL3B1YmxpYy9pdHJ1c2NybD9DQT0xQkQ0MjIwRTUwREJD
MDRCMDZBRDM5NzU0OTg0NkMwMUMzRThFQkQyJnNnPUhBQ0M0NzFCNjU0MjJFMTJC
MjdBOUQzM0E4N0FEMUNERjU5MjZFMTQwMzcxMA0GCSqGSIb3DQEBCwUAA4IBAQCv
bdw6QwSePVI64eTddCW8pY9Dbi66IjUcqgvjwDzHIUQCMx0NHBGJRCmN0f/f5xpM
nVg4I7RAkQ7WgFM78+MYUFJ5II368El8vTnhzRQ3Fv27v22G1EE/RAl7cQF9VB0X
j1gaB09pPqfMD40Mg522CNh7NNVqCPr3mlTAj2PMalCT7VhDDqUxmqm9RtBd9ARN
FMqx+FrUuFR5Y0N5o0OVcMbGcl8/krLqeUSZwy1Y42+KbViEqRxjTTOJEw/0B6TS
6nUIBddt24ucWnOmlkhfs8Xf4YbLSP52H9EfSOgZ4vZ2OZ0kBngbZzZgLSFM2LNk
UEBJbv+ElQBvKr2GP+EC
-----END CERTIFICATE-----

View File

@ -0,0 +1,9 @@
package com.wzj.soopin.transaction.business;
import com.wzj.soopin.transaction.domain.bo.ChargeBO;
import com.wzj.soopin.transaction.domain.vo.ChargeVO;
import org.dromara.common.web.core.IBusiness;
public interface IChargeBusiness extends IBusiness<ChargeVO, ChargeBO> {
boolean charge(ChargeBO bo);
}

View File

@ -0,0 +1,37 @@
package com.wzj.soopin.transaction.business.impl;
import com.wzj.soopin.transaction.business.IChargeBusiness;
import com.wzj.soopin.transaction.convert.ChargeConvert;
import com.wzj.soopin.transaction.domain.bo.ChargeBO;
import com.wzj.soopin.transaction.domain.po.Charge;
import com.wzj.soopin.transaction.domain.vo.ChargeVO;
import com.wzj.soopin.transaction.enums.ChargeStatus;
import com.wzj.soopin.transaction.service.IChargeService;
import com.wzj.soopin.transaction.util.SnowFlake;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.domain.model.LoginUser;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BusinessImpl;
import org.springframework.stereotype.Service;
@Service
@Slf4j
@AllArgsConstructor
public class ChargeBusinessImpl extends BusinessImpl<IChargeService, ChargeConvert, ChargeVO, ChargeBO, Charge> implements IChargeBusiness {
@Override
public boolean charge(ChargeBO bo) {
//先检查是否发起过如果发起过则返回原订单
Charge existCharge = service.getByCode(bo.getCode());
if (existCharge!=null){
return true;
}
Charge charge = converter.toPo(bo);
service.charge(charge);
bo.setCode(charge.getCode());
return true;
}
}

View File

@ -68,7 +68,7 @@ public class Charge extends BaseAudit {
private LocalDateTime auditTime;
/**
* 提现方式
* 充值方式
*/
private Integer method;

View File

@ -99,4 +99,6 @@ public class PayOrder extends BaseAudit {
@TableLogic(value = "0", delval = "1")
@TableField(value = "del_flag", fill = FieldFill.INSERT, jdbcType = JdbcType.CHAR)
private String delFlag;
private String title;
}

View File

@ -4,9 +4,10 @@ import lombok.Getter;
@Getter
public enum ChargeStatus {
WAITING(0, "待验证"),
PENDING(1, "充值成功"),
SUCCESS(2, "充值失败");
WAITING(0, "未支付"),
PENDING(1, "待验证"),
SUCCESS(2, "充值成功"),
FAIL(3, "充值失败");
private Integer code;
private String message;

View File

@ -0,0 +1,29 @@
package com.wzj.soopin.transaction.enums;
/**
* 订单状态枚举
*
* @author Chopper
* @since 2020/11/17 7:28 下午
*/
public enum PayStatusEnum {
/**
* 支付状态
*/
UNPAID("待付款"),
PAID("已付款"),
CANCEL("已取消"),
REFUNDED("已退款");
private final String description;
PayStatusEnum(String description) {
this.description = description;
}
public String description() {
return this.description;
}
}

View File

@ -1,5 +1,7 @@
package com.wzj.soopin.transaction.enums;
import jakarta.persistence.criteria.CriteriaBuilder;
/**
* 支付方式枚举
*
@ -11,23 +13,23 @@ public enum PaymentMethodEnum {
/**
* 微信
*/
WECHAT("wechatPlugin", "微信"),
WECHAT("wechatPlugin", 1),
/**
* 支付宝
*/
ALIPAY("aliPayPlugin", "支付宝"),
ALIPAY("aliPayPlugin", 2),
/**
* 余额支付
*/
WALLET("walletPlugin", "余额支付"),
WALLET("walletPlugin", 3),
/**
* 线下转账
*/
BANK_TRANSFER("bankTransferPlugin", "线下转账"),
BANK_TRANSFER("bankTransferPlugin", 4),
/**
* 易生支付
*/
EASY_PAY("easyPayPlugin", "易生支付");
EASY_PAY("easyPayPlugin", 5);
/**
* 插件id 调用对象需要实现payment接口
@ -36,14 +38,14 @@ public enum PaymentMethodEnum {
/**
* 支付名称
*/
private final String paymentName;
private final Integer code;
public String getPlugin() {
return plugin;
}
public String paymentName() {
return paymentName;
public Integer getCode() {
return code;
}
/**
@ -61,9 +63,9 @@ public enum PaymentMethodEnum {
return null;
}
PaymentMethodEnum(String plugin, String paymentName) {
PaymentMethodEnum(String plugin, Integer code) {
this.plugin = plugin;
this.paymentName = paymentName;
this.code = code;
}
}

View File

@ -0,0 +1,58 @@
package com.wzj.soopin.transaction.kit;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* 微信支付设置
*
* @author Chopper
* @since 2020-12-02 10:08
*/
@Accessors(chain = true)
@Data
@Configuration
@ConfigurationProperties(prefix = "wechat.pay.v3")
public class WechatPaymentSetting {
/**
* APP应用id
*/
private String appId;
/**
* 小程序应用id
*/
private String mpAppId;
/**
* 服务号应用id
*/
private String serviceAppId;
/**
* 商户号
*/
private String mchId;
/**
* 私钥
*/
private String apiclient_key;
/**
* pem 证书
*/
private String apiclient_cert_pem;
/**
* p12 证书
*/
private String apiclient_cert_p12;
/**
* 商户证书序列号
*/
private String mchSerialNo;
/**
* apiv3私钥
*/
private String apiV3Key;
private String privateKeyPath;
}

View File

@ -377,9 +377,9 @@ public class WxPayKit {
if (signType == null) {
signType = SignType.MD5;
}
// String packageSign = createSign(packageParams, partnerKey, signType);
// 部分微信APP支付 提示签名错误 解开下方注释 替换上边的代码就好
String packageSign = createAppSign(packageParams, partnerKey);
String packageSign = createSign(packageParams, partnerKey, signType);
// 部分微信APP支付 提示签名错误 解开下方注释 替换上边的代码就好
// String packageSign = createAppSign(packageParams, partnerKey);
packageParams.put("sign", packageSign);
return packageParams;
}

View File

@ -44,7 +44,7 @@ public class DateTimeZoneUtil implements Serializable {
if (date == null) {
throw new Exception("date is not null");
}
// ZonedDateTime zonedDateTime = DateTimeConverterUtil.toZonedDateTime(date);
// ZonedDateTime zonedDateTime = Date(date);
// time = DateTimeFormatterUtil.format(zonedDateTime, DateTimeFormatterUtil.YYYY_MM_DD_T_HH_MM_SS_XXX_FMT);
return time;
}

View File

@ -1,5 +1,6 @@
package com.wzj.soopin.transaction.kit.dto;
import com.wzj.soopin.transaction.kit.params.dto.CashierParam;
import io.swagger.annotations.ApiModelProperty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@ -22,7 +23,6 @@ public class PayParam {
@ApiModelProperty(value = "交易类型", allowableValues = "TRADE,ORDER,RECHARGE")
private String orderType;
@NotNull
@ApiModelProperty(value = "订单号")
private String sn;
@ -34,5 +34,4 @@ public class PayParam {
private String paymentClient;
}

View File

@ -21,7 +21,7 @@ import java.util.List;
public class CashierParam {
@ApiModelProperty(value = "价格")
private BigDecimal price;
private Long price;
@ApiModelProperty(value = "支付title")
private String title;

View File

@ -63,7 +63,7 @@ public class OrderCashier implements CashierExecute {
if (!order.getStatus().equals(OrderStatusEnum.UNPAID.getValue())) {
throw new ServiceException(ResultCode.PAY_BAN);
}
cashierParam.setPrice(order.getPayAmount());
cashierParam.setPrice(order.getPayAmount().longValue());
//
// try {
// BaseSetting baseSetting = JSONUtil.toBean(settingService.get(SettingEnum.BASE_SETTING.name()).getSettingValue(), BaseSetting.class);

View File

@ -2,17 +2,27 @@ package com.wzj.soopin.transaction.kit.params.impl;
import cn.hutool.json.JSONUtil;
import com.wzj.soopin.transaction.domain.po.Charge;
import com.wzj.soopin.transaction.domain.po.PayOrder;
import com.wzj.soopin.transaction.enums.CashierEnum;
import com.wzj.soopin.transaction.enums.ChargeStatus;
import com.wzj.soopin.transaction.enums.PayStatusEnum;
import com.wzj.soopin.transaction.kit.dto.PayParam;
import com.wzj.soopin.transaction.kit.dto.PaymentSuccessParams;
import com.wzj.soopin.transaction.kit.params.CashierExecute;
import com.wzj.soopin.transaction.kit.params.dto.CashierParam;
import com.wzj.soopin.transaction.mapper.ChargeMapper;
import com.wzj.soopin.transaction.mapper.PayOrderMapper;
import com.wzj.soopin.transaction.service.IChargeService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.ResultCode;
import org.dromara.common.core.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
/**
* 充值信息获取
*
@ -21,17 +31,12 @@ import org.springframework.stereotype.Component;
*/
@Slf4j
@Component
@AllArgsConstructor
public class RechargeCashier implements CashierExecute {
/**
* 余额
*/
// @Autowired
// private RechargeService rechargeService;
// /**
// * 设置
// */
// @Autowired
// private SettingService settingService;
private final IChargeService chargeService;
@Override
@ -55,25 +60,22 @@ public class RechargeCashier implements CashierExecute {
//准备返回的数据
CashierParam cashierParam = new CashierParam();
//订单信息获取
// Recharge recharge = rechargeService.getRecharge(payParam.getSn());
//
// //如果订单已支付则不能发器支付
// if (recharge.getPayStatus().equals(PayStatusEnum.PAID.name())) {
// throw new ServiceException(ResultCode.PAY_DOUBLE_ERROR);
// }
//
//
// cashierParam.setPrice(recharge.getRechargeMoney());
//
// try {
// BaseSetting baseSetting = JSONUtil.toBean(settingService.get(SettingEnum.BASE_SETTING.name()).getSettingValue(), BaseSetting.class);
// cashierParam.setTitle(baseSetting.getSiteName());
// } catch (Exception e) {
// cashierParam.setTitle("多用户商城,在线充值");
// }
// cashierParam.setDetail("余额充值");
// cashierParam.setCreateTime(recharge.getCreateTime());
// return cashierParam;
Charge recharge = chargeService.getByCode(payParam.getSn());
//如果订单已支付则不能发器支付
if (!recharge.getStatus().equals(ChargeStatus.WAITING.getCode())) {
throw new ServiceException(ResultCode.PAY_DOUBLE_ERROR);
}
cashierParam.setPrice( recharge.getMoney().longValue());
try {
cashierParam.setTitle("在线充值");
} catch (Exception e) {
cashierParam.setTitle("多用户商城,在线充值");
}
cashierParam.setDetail("余额充值");
cashierParam.setCreateTime(recharge.getCreateTime());
return cashierParam;
}
return null;

View File

@ -290,7 +290,7 @@ public class EasyPayPlugin implements Payment {
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
//支付金额
BigDecimal fen =cashierParam.getPrice();
Long fen =cashierParam.getPrice();
//第三方付款订单
String outOrderNo = SnowFlake.getIdStr();
//过期时间
@ -312,7 +312,7 @@ public class EasyPayPlugin implements Payment {
//回传参数
.setAttach(attach)
.setNotify_url(notifyUrl("apiProperties.getBuyer()", PaymentMethodEnum.WECHAT))
.setAmount(new Amount().setTotal(fen));
.setAmount(new Amount().setTotal(fen.intValue()));
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
// PaymentHttpResponse response = WechatApi.v3(

View File

@ -28,6 +28,8 @@ import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
/**
* WalletPlugin
@ -163,7 +165,7 @@ public class WalletPlugin implements Payment {
}
//个人账户扣减
boolean result = accountBillService.reduceMoney(
cashierParam.getPrice(),
BigDecimal.valueOf(cashierParam.getPrice() ),
loginUser.getUserId(),
AccountBillSourceEnum.PAYMENT,

View File

@ -7,16 +7,16 @@ import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wzj.soopin.order.utils.StringUtils;
import com.wzj.soopin.transaction.domain.po.RefundLog;
import com.wzj.soopin.transaction.enums.PaymentMethodEnum;
import com.wzj.soopin.transaction.kit.CashierSupport;
import com.wzj.soopin.transaction.kit.Payment;
import com.wzj.soopin.transaction.kit.WechatPaymentSetting;
import com.wzj.soopin.transaction.kit.core.PaymentHttpResponse;
import com.wzj.soopin.transaction.kit.core.enums.RequestMethodEnums;
import com.wzj.soopin.transaction.kit.core.enums.SignType;
import com.wzj.soopin.transaction.kit.core.kit.HttpKit;
import com.wzj.soopin.transaction.kit.core.kit.IpKit;
import com.wzj.soopin.transaction.kit.core.kit.WxPayKit;
import com.wzj.soopin.transaction.kit.core.kit.*;
import com.wzj.soopin.transaction.kit.core.utils.DateTimeZoneUtil;
import com.wzj.soopin.transaction.kit.dto.PayParam;
import com.wzj.soopin.transaction.kit.dto.PaymentSuccessParams;
@ -27,20 +27,31 @@ import com.wzj.soopin.transaction.kit.plugin.wechat.model.*;
import com.wzj.soopin.transaction.service.PaymentService;
import com.wzj.soopin.transaction.service.RefundLogService;
import com.wzj.soopin.transaction.util.CurrencyUtil;
import com.wzj.soopin.transaction.util.SnowFlake;
import com.wzj.soopin.transaction.wechat.WechatPayConfig;
import com.wzj.soopin.transaction.wechat.WechatPayException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.CachePrefix;
import org.dromara.common.core.constant.ResultCode;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.redis.redis.RedisCache;
import org.dromara.common.redis.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* 微信支付
@ -94,6 +105,10 @@ public class WechatPlugin implements Payment {
// @Autowired
// private OrderService orderService;
@Autowired
private WechatPaymentSetting setting;
@Autowired
private RedisCache redisCache;
@Override
public R<Object> h5pay(HttpServletRequest request, HttpServletResponse response1, PayParam payParam) {
@ -236,63 +251,68 @@ public class WechatPlugin implements Payment {
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
//支付金额
// Integer fen = CurrencyUtil.fen(cashierParam.getPrice());
// //第三方付款订单
// String outOrderNo = SnowFlake.getIdStr();
// //过期时间
// String timeExpire = DateTimeZoneUtil.dateToTimeZone(System.currentTimeMillis() + 1000 * 60 * 3);
//
// String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
//
// WechatPaymentSetting setting = wechatPaymentSetting();
// String appid = setting.getAppId();
// if (appid == null) {
// throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
// }
// UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
// .setAppid(appid)
// .setMchid(setting.getMchId())
// .setDescription(cashierParam.getDetail())
// .setOut_trade_no(outOrderNo)
// .setTime_expire(timeExpire)
// .setAttach(attach)
// .setNotify_url(notifyUrl(apiProperties.getBuyer(), PaymentMethodEnum.WECHAT))
// .setAmount(new Amount().setTotal(fen));
//
//
// log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
// PaymentHttpResponse response = WechatApi.v3(
// RequestMethodEnums.POST,
// WechatDomain.CHINA.toString(),
// WechatApiEnum.APP_PAY.toString(),
// setting.getMchId(),
// setting.getSerialNumber(),
// null,
// setting.getApiclient_key(),
// JSONUtil.toJsonStr(unifiedOrderModel)
// );
// //根据证书序列号查询对应的证书来验证签名结果
// boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
// log.info("verifySignature: {}", verifySignature);
// log.info("统一下单响应 {}", response);
//
// if (verifySignature) {
// JSONObject jsonObject = JSONUtil.parseObj(response.getBody());
// String prepayId = jsonObject.getStr("prepay_id");
// Map<String, String> map = WxPayKit.appPrepayIdCreateSign(appid,
// setting.getMchId(),
// prepayId,
// setting.getApiclient_key(), SignType.HMACSHA256);
// log.info("唤起支付参数:{}", map);
//
// return R.ok(map);
// }
Integer fen = cashierParam.getPrice().intValue();
//第三方付款订单
String outOrderNo = SnowFlake.getIdStr();
//过期时间
ZonedDateTime zonedDateTime = LocalDateTime.now().plusMinutes(3).atZone(ZoneId.of("UTC"));
// 自定义无毫秒的 RFC 3339 格式器
DateTimeFormatter rfc3339NoMillis = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
String timeExpire = zonedDateTime.format(rfc3339NoMillis);
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam.getSn()), StandardCharsets.UTF_8);
String appid = setting.getAppId();
if (appid == null) {
throw new ServiceException(ResultCode.WECHAT_PAYMENT_NOT_SETTING);
}
UnifiedOrderModel unifiedOrderModel = new UnifiedOrderModel()
.setAppid(appid)
.setMchid(setting.getMchId())
.setDescription(cashierParam.getDetail())
.setOut_trade_no(outOrderNo)
.setTime_expire(timeExpire)
.setAttach(attach)
.setNotify_url("http://cjh.wuzhongjie.com.cn/app/wechat/notify")
.setAmount(new Amount().setTotal(fen));
log.info("统一下单参数 {}", JSONUtil.toJsonStr(unifiedOrderModel));
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.APP_PAY.toString(),
setting.getMchId(),
setting.getMchSerialNo(),
setting.getMchSerialNo(),
setting.getPrivateKeyPath() ,
JSONUtil.toJsonStr(unifiedOrderModel)
);
//根据证书序列号查询对应的证书来验证签名结果
boolean verifySignature = WxPayKit.verifySignature(response, getPlatformCert());
log.info("verifySignature: {}", verifySignature);
log.info("统一下单响应 {}", response);
if (verifySignature) {
JSONObject jsonObject = JSONUtil.parseObj(response.getBody());
String prepayId = jsonObject.getStr("prepay_id");
Map<String, String> map = WxPayKit.appPrepayIdCreateSign(appid,
setting.getMchId(),
prepayId,
setting.getApiclient_key(), SignType.HMACSHA256);
log.info("唤起支付参数:{}", map);
return R.ok(map);
}
log.error("微信支付参数验证错误,请及时处理");
throw new ServiceException(ResultCode.PAY_ERROR);
} catch (Exception e) {
log.error("支付异常", e);
throw new ServiceException(ResultCode.PAY_ERROR);
}
}
@Override
@ -614,81 +634,80 @@ public class WechatPlugin implements Payment {
// }
// }
// /**
// * 获取平台公钥
// *
// * @return 平台公钥
// */
// private X509Certificate getPlatformCert() {
// //获取缓存中的平台公钥如果有则直接返回否则去微信请求
// String publicCert = cache.getString(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix());
// if (!StringUtils.isEmpty(publicCert)) {
// return PayKit.getCertificate(publicCert);
// }
// //获取平台证书列表
// try {
//
// WechatPaymentSetting setting = wechatPaymentSetting();
//
// PaymentHttpResponse response = WechatApi.v3(
// RequestMethodEnums.GET,
// WechatDomain.CHINA.toString(),
// WechatApiEnum.GET_CERTIFICATES.toString(),
// setting.getMchId(),
// setting.getSerialNumber(),
// null,
// setting.getApiclient_key(),
// ""
// );
// String body = response.getBody();
// log.info("获取微信平台证书body: {}", body);
//
// if (response.getStatus() == 200) {
// JSONObject jsonObject = JSONUtil.parseObj(body);
// JSONArray dataArray = jsonObject.getJSONArray("data");
// log.info("证书信息: {}", dataArray);
//
// //默认认为只有一个平台证书
// JSONObject encryptObject = dataArray.getJSONObject(0);
// JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
// String associatedData = encryptCertificate.getStr("associated_data");
// String cipherText = encryptCertificate.getStr("ciphertext");
// String nonce = encryptCertificate.getStr("nonce");
// publicCert = getPlatformCertStr(associatedData, nonce, cipherText);
// long second = (PayKit.getCertificate(publicCert).getNotAfter().getTime() - System.currentTimeMillis()) / 1000;
// cache.put(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix(), publicCert, second);
// } else {
// log.error("证书获取失败:{}" + body);
// throw new ServiceException(ResultCode.WECHAT_CERT_ERROR);
// }
// return PayKit.getCertificate(publicCert);
// } catch (Exception e) {
// log.error("证书获取失败", e);
// }
// return null;
// }
//
// /**
// * 获取平台证书缓存的字符串
// * 下列各个密钥参数
// *
// * @param associatedData 密钥参数
// * @param nonce 密钥参数
// * @param cipherText 密钥参数
// * @return platform key
// * @throws GeneralSecurityException 密钥获取异常
// */
// private String getPlatformCertStr(String associatedData, String nonce, String cipherText) throws GeneralSecurityException {
//
//
// AesUtil aesUtil = new AesUtil(wechatPaymentSetting().getApiKey3().getBytes(StandardCharsets.UTF_8));
// //平台证书密文解密
// //encrypt_certificate 中的 associated_data nonce ciphertext
// return aesUtil.decryptToString(
// associatedData.getBytes(StandardCharsets.UTF_8),
// nonce.getBytes(StandardCharsets.UTF_8),
// cipherText
// );
// }
/**
* 获取平台公钥
*
* @return 平台公钥
*/
private X509Certificate getPlatformCert() {
//获取缓存中的平台公钥如果有则直接返回否则去微信请求
String publicCert = RedisUtils.getCacheObject(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix());
if (!StringUtils.isEmpty(publicCert)) {
return PayKit.getCertificate(publicCert);
}
//获取平台证书列表
try {
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.GET,
WechatDomain.CHINA.toString(),
WechatApiEnum.GET_CERTIFICATES.toString(),
setting.getMchId(),
setting.getMchSerialNo(),
null,
setting.getPrivateKeyPath(),
""
);
String body = response.getBody();
log.info("获取微信平台证书body: {}", body);
if (response.getStatus() == 200) {
JSONObject jsonObject = JSONUtil.parseObj(body);
JSONArray dataArray = jsonObject.getJSONArray("data");
log.info("证书信息: {}", dataArray);
//默认认为只有一个平台证书
JSONObject encryptObject = dataArray.getJSONObject(0);
JSONObject encryptCertificate = encryptObject.getJSONObject("encrypt_certificate");
String associatedData = encryptCertificate.getStr("associated_data");
String cipherText = encryptCertificate.getStr("ciphertext");
String nonce = encryptCertificate.getStr("nonce");
publicCert = getPlatformCertStr(associatedData, nonce, cipherText);
long second = (PayKit.getCertificate(publicCert).getNotAfter().getTime() - System.currentTimeMillis()) / 1000;
redisCache.setCacheObject(CachePrefix.WECHAT_PLAT_FORM_CERT.getPrefix(), publicCert, (int) second, TimeUnit.SECONDS);
} else {
log.error("证书获取失败:{}" + body);
throw new ServiceException(ResultCode.WECHAT_CERT_ERROR);
}
return PayKit.getCertificate(publicCert);
} catch (Exception e) {
log.error("证书获取失败", e);
}
return null;
}
/**
* 获取平台证书缓存的字符串
* 下列各个密钥参数
*
* @param associatedData 密钥参数
* @param nonce 密钥参数
* @param cipherText 密钥参数
* @return platform key
* @throws GeneralSecurityException 密钥获取异常
*/
private String getPlatformCertStr(String associatedData, String nonce, String cipherText) throws GeneralSecurityException {
AesUtil aesUtil = new AesUtil(setting.getApiV3Key().getBytes(StandardCharsets.UTF_8));
//平台证书密文解密
//encrypt_certificate 中的 associated_data nonce ciphertext
return aesUtil.decryptToString(
associatedData.getBytes(StandardCharsets.UTF_8),
nonce.getBytes(StandardCharsets.UTF_8),
cipherText
);
}
}

View File

@ -18,7 +18,7 @@ public class Amount {
/**
* 总金额
*/
private BigDecimal total;
private Integer total;
/**
* 货币类型

View File

@ -9,4 +9,6 @@ public interface IChargeService extends IService<Charge> {
boolean charge(Charge charge);
Charge getByCode(String code);
}

View File

@ -1,5 +1,6 @@
package com.wzj.soopin.transaction.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzj.soopin.member.domain.po.AccountBill;
import com.wzj.soopin.member.domain.po.MemberAccount;
@ -11,6 +12,7 @@ import com.wzj.soopin.transaction.domain.po.Charge;
import com.wzj.soopin.transaction.enums.ChargeStatus;
import com.wzj.soopin.transaction.mapper.ChargeMapper;
import com.wzj.soopin.transaction.service.IChargeService;
import com.wzj.soopin.transaction.util.SnowFlake;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
@ -44,13 +46,13 @@ public class ChargeServiceImpl extends ServiceImpl<ChargeMapper, Charge> impleme
memberAccount.setWallet(memberAccount.getWallet().add(charge.getMoney()));
memberAccountService.updateById(memberAccount);
//生成充值记录
AccountBill accountBill=AccountBill.builder()
.accountId(charge.getMemberId())
.changeAmount(charge.getMoney())
.changeType(AccountBillChangeTypeEnum.IN.getCode())
.changeDesc("充值")
.source(AccountBillSourceEnum.RECHARGE.getCode())
.build();
AccountBill accountBill = AccountBill.builder()
.accountId(charge.getMemberId())
.changeAmount(charge.getMoney())
.changeType(AccountBillChangeTypeEnum.IN.getCode())
.changeDesc("充值")
.source(AccountBillSourceEnum.RECHARGE.getCode())
.build();
accountBillService.save(accountBill);
return true;
}
@ -62,18 +64,29 @@ public class ChargeServiceImpl extends ServiceImpl<ChargeMapper, Charge> impleme
@Override
public boolean charge(Charge charge) {
//判断充值金额不能为零
if (charge.getMoney().compareTo(BigDecimal.ZERO) == 0) {
if (charge.getMoney()==null||charge.getMoney().compareTo(BigDecimal.ZERO) == 0) {
throw new ServiceException("充值金额不能为零");
}
//状态为待审核
//状态为待充值
charge.setStatus(ChargeStatus.WAITING.getCode());
charge.setType(1);
//手续费
charge.setFee(new BigDecimal("0.01").multiply(charge.getMoney()));
//实际金额
charge.setActualMoney(charge.getMoney().subtract(charge.getFee()));
//设置充值信息
if (charge.getMemberId() == null) {
throw new ServiceException("充值用户不能为空");
}
charge.setCode(SnowFlake.createStr("RC"));
//保存充值记录
save(charge);
return false;
}
@Override
public Charge getByCode(String code) {
return baseMapper.selectOne(new LambdaQueryWrapper<Charge>().eq(Charge::getCode, code));
}
}

View File

@ -1,5 +1,6 @@
package com.wzj.soopin.transaction.service.impl;
import com.wzj.soopin.transaction.enums.TransState;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzj.soopin.transaction.domain.po.PayOrder;
@ -8,4 +9,10 @@ import com.wzj.soopin.transaction.service.PayOrderService;
@Service
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements PayOrderService{
@Override
public boolean save(PayOrder entity) {
//
entity.setTransState(TransState.PENDING.getCode());
return super.save(entity);
}
}

View File

@ -150,7 +150,7 @@ public class WithdrawServiceImpl extends ServiceImpl<WithdrawMapper, Withdraw> i
}
//检查当前用于的账户余额是否充足
BigDecimal balance = memberAccount.getWallet();
if (balance.compareTo(withdraw.getMoney()) < 0) {
if (balance==null||balance.compareTo(withdraw.getMoney()) < 0) {
throw new RuntimeException("用户余额不足");
}
//生成费用

View File

@ -1,5 +1,7 @@
package com.wzj.soopin.transaction.util;
import com.google.type.Decimal;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
@ -120,9 +122,8 @@ public final class CurrencyUtil {
* @param money 金额
* @return 转换单位为分
*/
public static Integer fen(Double money) {
double price = mul(money, 100);
return (int) price;
public static Integer fen(BigDecimal money) {
return money.multiply(BigDecimal.valueOf(100)).intValue();
}
/**

View File

@ -47,7 +47,7 @@ public class SnowFlake {
* @return
*/
public static String createStr(String prefix) {
return prefix + DateUtil.toString(new Date(), "yyyyMMdd") + SnowFlake.getId();
return prefix + DateUtil.toString(new Date(), "yyyyMMdd") + SnowFlake.getIdStr();
}
public static String getIdStr() {

View File

@ -204,7 +204,11 @@ http {
index index.html;
try_files $uri $uri/ /busniess/index.html;
}
location ^~ /file/ {
proxy_pass http://43.143.227.203:9000/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
# app分享
location ^~ /spa {
alias /data/wzj/download/spa;