微信、支付宝提现

This commit is contained in:
pikachu1995@126.com 2023-02-23 17:50:56 +08:00
parent 0a3696ac56
commit b8398ac1d1
26 changed files with 1289 additions and 2 deletions

View File

@ -18,6 +18,7 @@ import cn.lili.modules.system.entity.dto.payment.PaymentSupportSetting;
import cn.lili.modules.system.entity.dto.payment.dto.PaymentSupportItem;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import cn.lili.modules.wallet.entity.dos.MemberWithdrawApply;
import cn.lili.modules.wallet.service.MemberWalletService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -142,6 +143,15 @@ public class CashierSupport {
payment.notify(request);
}
/**
* 用户提现
* @param paymentMethodEnum 支付渠道
* @param memberWithdrawApply 用户提现申请
*/
public void transfer(PaymentMethodEnum paymentMethodEnum, MemberWithdrawApply memberWithdrawApply){
Payment payment = (Payment) SpringContextUtil.getBean(paymentMethodEnum.getPlugin());
payment.transfer(memberWithdrawApply);
}
/**
* 获取收银台参数
*

View File

@ -6,6 +6,7 @@ import cn.lili.common.vo.ResultMessage;
import cn.lili.modules.payment.entity.RefundLog;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.payment.kit.dto.PayParam;
import cn.lili.modules.wallet.entity.dos.MemberWithdrawApply;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -110,6 +111,13 @@ public interface Payment {
throw new ServiceException(ResultCode.PAY_ERROR);
}
/**
* 提现
*/
default void transfer(MemberWithdrawApply memberWithdrawApply) {
throw new ServiceException(ResultCode.PAY_ERROR);
}
/**
* 支付回调地址
*

View File

@ -26,11 +26,13 @@ import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.payment.AlipayPaymentSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import cn.lili.modules.wallet.entity.dos.MemberWithdrawApply;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.*;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.response.AlipayTradeCancelResponse;
import com.alipay.api.request.AlipayFundTransUniTransferRequest;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -229,6 +231,43 @@ public class AliPayPlugin implements Payment {
log.info("支付异步通知:");
}
/**
* 支付宝提现
* 文档地址https://opendocs.alipay.com/open/02byuo?scene=ca56bca529e64125a2786703c6192d41&ref=api
*
* @param memberWithdrawApply 会员提现申请
*/
@Override
public void transfer(MemberWithdrawApply memberWithdrawApply) {
AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel();
model.setOutBizNo(SnowFlake.createStr("T"));
model.setRemark("用户提现");
model.setBusinessParams("{\"payer_show_name_use_alias\":\"true\"}");
model.setBizScene("DIRECT_TRANSFER");
Participant payeeInfo = new Participant();
payeeInfo.setIdentity(memberWithdrawApply.getConnectNumber());
payeeInfo.setIdentityType("ALIPAY_LOGON_ID");
payeeInfo.setName(memberWithdrawApply.getRealName());
model.setPayeeInfo(payeeInfo);
model.setTransAmount(memberWithdrawApply.getApplyMoney().toString());
model.setProductCode("TRANS_ACCOUNT_NO_PWD");
model.setOrderTitle("用户提现");
//交互退款
try {
AlipayFundTransUniTransferResponse alipayFundTransUniTransferResponse = AliPayApi.uniTransferToResponse(model,null);
log.error("支付宝退款,参数:{},支付宝响应:{}", JSONUtil.toJsonStr(model), JSONUtil.toJsonStr(alipayFundTransUniTransferResponse));
if (alipayFundTransUniTransferResponse.isSuccess()) {
} else {
log.error(alipayFundTransUniTransferResponse.getSubMsg());
}
} catch (Exception e) {
log.error("用户提现异常:", e);
throw new ServiceException(ResultCode.PAY_ERROR);
}
}
/**
* 验证支付结果
*

View File

@ -0,0 +1,38 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付接口</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay;
import cn.lili.modules.payment.kit.core.kit.HttpKit;
import cn.lili.modules.payment.kit.core.kit.WxPayKit;
import java.util.Map;
public class UnionPayApi {
public static String authUrl = "https://qr.95516.com/qrcGtwWeb-web/api/userAuth?version=1.0.0&redirectUrl=%s";
public static String execution(String url, Map<String, String> params) {
return HttpKit.getDelegate().post(url, WxPayKit.toXml(params));
}
/**
* 获取用户授权 API
*
* @param url 回调地址可以自定义参数 https://pay.javen.com/callback?sdk=ijpay
* @return 银联重定向 Url
*/
public static String buildAuthUrl(String url) {
return String.format(authUrl, url);
}
}

View File

@ -0,0 +1,57 @@
package cn.lili.modules.payment.kit.plugin.unionpay;
import lombok.*;
import java.io.Serializable;
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付等常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付常用配置</p>
*
* @author Javen
*/
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class UnionPayApiConfig implements Serializable {
private static final long serialVersionUID = -9025648880068727445L;
/**
* 商户平台分配的账号
*/
private String mchId;
/**
* 连锁商户号
*/
private String groupMchId;
/**
* 授权交易机构代码
*/
private String agentMchId;
/**
* 商户平台分配的密钥
*/
private String apiKey;
/**
* 商户平台网关
*/
private String serverUrl;
/**
* 应用域名回调中会使用此参数
*/
private String domain;
/**
* 其他附加参数
*/
private Object exParams;
}

View File

@ -0,0 +1,87 @@
package cn.lili.modules.payment.kit.plugin.unionpay;
import cn.hutool.core.util.StrUtil;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付等常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付常用配置 Kit</p>
*
* @author Javen
*/
public class UnionPayApiConfigKit {
private static final ThreadLocal<String> TL = new ThreadLocal<String>();
private static final Map<String, UnionPayApiConfig> CFG_MAP = new ConcurrentHashMap<String, UnionPayApiConfig>();
private static final String DEFAULT_CFG_KEY = "_default_key_";
/**
* 添加云闪付配置每个 mchId 只需添加一次相同 mchId 将被覆盖
*
* @param UnionPayApiConfig 云闪付配置
* @return {@link UnionPayApiConfig} 云闪付配置
*/
public static UnionPayApiConfig putApiConfig(UnionPayApiConfig UnionPayApiConfig) {
if (CFG_MAP.size() == 0) {
CFG_MAP.put(DEFAULT_CFG_KEY, UnionPayApiConfig);
}
return CFG_MAP.put(UnionPayApiConfig.getMchId(), UnionPayApiConfig);
}
public static UnionPayApiConfig setThreadLocalApiConfig(UnionPayApiConfig UnionPayApiConfig) {
if (StrUtil.isNotEmpty(UnionPayApiConfig.getMchId())) {
setThreadLocalMchId(UnionPayApiConfig.getMchId());
}
return putApiConfig(UnionPayApiConfig);
}
public static UnionPayApiConfig removeApiConfig(UnionPayApiConfig UnionPayApiConfig) {
return removeApiConfig(UnionPayApiConfig.getMchId());
}
public static UnionPayApiConfig removeApiConfig(String mchId) {
return CFG_MAP.remove(mchId);
}
public static void setThreadLocalMchId(String mchId) {
if (StrUtil.isEmpty(mchId)) {
mchId = CFG_MAP.get(DEFAULT_CFG_KEY).getMchId();
}
TL.set(mchId);
}
public static void removeThreadLocalMchId() {
TL.remove();
}
public static String getMchId() {
String appId = TL.get();
if (StrUtil.isEmpty(appId)) {
appId = CFG_MAP.get(DEFAULT_CFG_KEY).getMchId();
}
return appId;
}
public static UnionPayApiConfig getApiConfig() {
String appId = getMchId();
return getApiConfig(appId);
}
public static UnionPayApiConfig getApiConfig(String appId) {
UnionPayApiConfig cfg = CFG_MAP.get(appId);
if (cfg == null) {
throw new IllegalStateException("需事先调用 UnionPayApiConfigKit.putApiConfig(UnionPayApiConfig) 将 mchId 对应的 UnionPayApiConfig 对象存入,才可以使用 UnionPayApiConfigKit.getUnionPayApiConfig() 的系列方法");
}
return cfg;
}
}

View File

@ -0,0 +1,247 @@
package cn.lili.modules.payment.kit.plugin.unionpay;
import cn.hutool.core.net.URLEncoder;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.utils.SnowFlake;
import cn.lili.common.vo.ResultMessage;
import cn.lili.modules.payment.kit.CashierSupport;
import cn.lili.modules.payment.kit.Payment;
import cn.lili.modules.payment.kit.core.enums.SignType;
import cn.lili.modules.payment.kit.core.kit.HttpKit;
import cn.lili.modules.payment.kit.core.kit.IpKit;
import cn.lili.modules.payment.kit.core.kit.WxPayKit;
import cn.lili.modules.payment.kit.dto.PayParam;
import cn.lili.modules.payment.kit.params.dto.CashierParam;
import cn.lili.modules.payment.kit.plugin.unionpay.enums.ServiceEnum;
import cn.lili.modules.payment.kit.plugin.unionpay.model.UnifiedOrderModel;
import cn.lili.modules.payment.service.PaymentService;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.payment.UnionPaymentSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
* 银联云闪付 支付
*
* @author Bulbasaur
* @since 2023/02/14 17:44
*/
@Slf4j
@Component
public class UnionPayPlugin implements Payment {
/**
* 收银台
*/
@Autowired
private CashierSupport cashierSupport;
/**
* 支付日志
*/
@Autowired
private PaymentService paymentService;
/**
* 配置
*/
@Autowired
private SettingService settingService;
@Override
public ResultMessage<Object> nativePay(HttpServletRequest request, PayParam payParam) {
try {
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
UnionPaymentSetting unionPaymentSetting = this.unionPaymentSetting();
String notifyUrl = unionPaymentSetting.getUnionPayDomain().concat("/unionPay/payNotify");
//用户ip
String ip = IpKit.getRealIp(request);
//第三方付款订单
String outOrderNo = SnowFlake.getIdStr();
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
Map<String, String> params = UnifiedOrderModel.builder()
.service(ServiceEnum.NATIVE.toString())
.mch_id(unionPaymentSetting.getUnionPayMachId())
.out_trade_no(outOrderNo)
.body(cashierParam.getDetail())
.attach(attach)
.total_fee("0")
.mch_create_ip(ip)
.notify_url(notifyUrl)
.nonce_str(WxPayKit.generateStr())
.build().createSign(unionPaymentSetting.getUnionPayKey(), SignType.MD5);
String xmlResult = UnionPayApi.execution(unionPaymentSetting.getUnionPayServerUrl(), params);
log.info("xmlResult:" + xmlResult);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
log.info(result.toString());
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public ResultMessage<Object> appPay(HttpServletRequest request, PayParam payParam) {
try {
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
UnionPaymentSetting unionPaymentSetting = this.unionPaymentSetting();
String notifyUrl = unionPaymentSetting.getUnionPayDomain().concat("/unionPay/payNotify");
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
//用户ip
String ip = IpKit.getRealIp(request);
Map<String, String> params = UnifiedOrderModel.builder()
.service(ServiceEnum.WEI_XIN_APP_PAY.toString())
.mch_id(unionPaymentSetting.getUnionPayMachId())
.appid(unionPaymentSetting.getUnionPayAppId())
.out_trade_no(WxPayKit.generateStr())
.body(cashierParam.getDetail())
.attach(attach)
.total_fee("0")
.mch_create_ip(ip)
.notify_url(notifyUrl)
.nonce_str(WxPayKit.generateStr())
.build()
.createSign(unionPaymentSetting.getUnionPayKey(), SignType.MD5);
System.out.println(params);
String xmlResult = UnionPayApi.execution(unionPaymentSetting.getUnionPayServerUrl(), params);
log.info("xmlResult:" + xmlResult);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
if (!WxPayKit.verifyNotify(result, unionPaymentSetting.getUnionPayKey(), SignType.MD5)) {
log.error("签名异常");
}
String status = result.get("status");
String resultCode = result.get("result_code");
if (!"0".equals(status) && !"0".equals(resultCode)) {
log.error(result.get("err_msg"));
return null;
}
log.error(result.get("pay_info"));
return null;
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
return null;
}
}
@Override
public ResultMessage<Object> jsApiPay(HttpServletRequest request, PayParam payParam) {
String buyerLogonId="";
String buyerId="";
CashierParam cashierParam = cashierSupport.cashierParam(payParam);
UnionPaymentSetting unionPaymentSetting = this.unionPaymentSetting();
String attach = URLEncoder.createDefault().encode(JSONUtil.toJsonStr(payParam), StandardCharsets.UTF_8);
//用户ip
String ip = IpKit.getRealIp(request);
try {
if (StrUtil.isEmpty(buyerLogonId) && StrUtil.isEmpty(buyerId)) {
log.error("buyer_logon_id buyer_id 不能同时为空");
return null;
}
String notifyUrl = unionPaymentSetting.getUnionPayDomain().concat("/unionPay/payNotify");
Map<String, String> params = UnifiedOrderModel.builder()
.service(ServiceEnum.ALI_PAY_JS_PAY.toString())
.mch_id(unionPaymentSetting.getUnionPayMachId())
.out_trade_no(WxPayKit.generateStr())
.body(cashierParam.getDetail())
.attach(attach)
.total_fee("0")
.mch_create_ip(ip)
.notify_url(notifyUrl)
.nonce_str(WxPayKit.generateStr())
.buyer_id(buyerId)
.buyer_logon_id(buyerLogonId)
.build()
.createSign(unionPaymentSetting.getUnionPayKey(), SignType.MD5);
System.out.println(params);
String xmlResult = UnionPayApi.execution(unionPaymentSetting.getUnionPayServerUrl(), params);
log.info("xmlResult:" + xmlResult);
Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
log.info(result.toString());
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Override
public void callBack(HttpServletRequest request) {
try {
verifyNotify(request);
} catch (Exception e) {
log.error("支付异常", e);
}
}
@Override
public void notify(HttpServletRequest request) {
try {
verifyNotify(request);
} catch (Exception e) {
log.error("支付异常", e);
}
}
private void verifyNotify(HttpServletRequest request) throws Exception {
String xmlMsg = HttpKit.readData(request);
log.info("支付通知=" + xmlMsg);
Map<String, String> params = WxPayKit.xmlToMap(xmlMsg);
String status = params.get("status");
String returnCode = params.get("result_code");
log.info(status + " " + returnCode);
if ("0".equals(status) && "0".equals(returnCode)) {
UnionPaymentSetting unionPaymentSetting = this.unionPaymentSetting();
// 注意重复通知的情况同一订单号可能收到多次通知请注意一定先判断订单状态
// 注意此处签名方式需与统一下单的签名类型一致
if (WxPayKit.verifyNotify(params, unionPaymentSetting.getUnionPayKey(), SignType.MD5)) {
log.info("支付成功....");
// 更新订单信息
// 发送通知等
}
}
}
/**
* 获取微信支付配置
*
* @return
*/
private UnionPaymentSetting unionPaymentSetting() {
try {
Setting systemSetting = settingService.get(SettingEnum.UNIONPAY_PAYMENT.name());
UnionPaymentSetting unionPaymentSetting = JSONUtil.toBean(systemSetting.getSettingValue(), UnionPaymentSetting.class);
return unionPaymentSetting;
} catch (Exception e) {
log.error("微信支付暂不支持", e);
throw new ServiceException(ResultCode.PAY_NOT_SUPPORT);
}
}
}

View File

@ -0,0 +1,95 @@
package cn.lili.modules.payment.kit.plugin.unionpay.enums;
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付接口类型枚举</p>
*
* @author Javen
*/
public enum ServiceEnum {
/**
* 刷卡支付
*/
MICRO_PAY("unified.trade.micropay"),
/**
* 扫码支付
*/
NATIVE("unified.trade.native"),
/**
* 微信公众号小程序支付统一下单
*/
WEI_XIN_JS_PAY("pay.weixin.jspay"),
/**
* 微信 App 支付
*/
WEI_XIN_APP_PAY("pay.weixin.raw.app"),
/**
* 查询订单
*/
QUERY("unified.trade.query"),
/**
* 申请退款
*/
REFUND("unified.trade.refund"),
/**
* 退款查询
*/
REFUND_QUERY("unified.trade.refundquery"),
/**
* 关闭订单
*/
CLOSE("unified.trade.close"),
/**
* 撤销订单
*/
MICRO_PAY_REVERSE("unified.micropay.reverse"),
/**
* 授权码查询 openid
*/
AUTH_CODE_TO_OPENID("unified.tools.authcodetoopenid"),
/**
* 银联 JS 支付获取 userId
*/
UNION_PAY_USER_ID("pay.unionpay.userid"),
/**
* 银联 JS 支付下单
*/
UNION_JS_PAY("pay.unionpay.jspay"),
/**
* 支付宝服务窗口支付
*/
ALI_PAY_JS_PAY("pay.alipay.jspay"),
/**
* 下载单个商户时的对账单
*/
BILL_MERCHANT("pay.bill.merchant"),
/**
* 下载连锁商户下所有门店的对账单
*/
BILL_BIG_MERCHANT("pay.bill.bigMerchant"),
/**
* 下载某内部机构/外包服务机构下所有商户的对账单
*/
BILL_AGENT("pay.bill.agent");
/**
* 接口类型
*/
private final String service;
ServiceEnum(String domain) {
this.service = domain;
}
@Override
public String toString() {
return service;
}
}

View File

@ -0,0 +1,55 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付-商户进件</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@AllArgsConstructor
@Getter
@Setter
public class ApplyModel extends BaseModel{
/**
* 合作伙伴 ID 即机构号
*/
private String partner;
/**
* 服务名称
*/
private String serviceName;
/**
* 支持 MD5 和RSA默认为MD5
*/
private String signType;
/**
* 字符集默认为UTF-8
*/
private String charset;
/**
* 请求数据
*/
private String data;
/**
* 数据类型
*/
private String dataType;
/**
* 数据签名
*/
private String dataSign;
}

View File

@ -0,0 +1,37 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付-授权码查询 openId</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@AllArgsConstructor
@Getter
@Setter
public class AuthCodeToOpenIdModel extends BaseModel{
private String service;
private String version;
private String charset;
private String sign_type;
private String mch_id;
private String sub_appid;
private String auth_code;
private String nonce_str;
private String sign;
private String sign_agentno;
private String groupno;
}

View File

@ -0,0 +1,105 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>Model 公用方法</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay.model;
import cn.hutool.core.util.StrUtil;
import cn.lili.modules.payment.kit.core.enums.SignType;
import cn.lili.modules.payment.kit.core.kit.WxPayKit;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class BaseModel {
/**
* 将建构的 builder 转为 Map
*
* @return 转化后的 Map
*/
public Map<String, String> toMap() {
String[] fieldNames = getFiledNames(this);
HashMap<String, String> map = new HashMap<String, String>(fieldNames.length);
for (String name : fieldNames) {
String value = (String) getFieldValueByName(name, this);
if (StrUtil.isNotEmpty(value)) {
map.put(name, value);
}
}
return map;
}
/**
* 构建签名 Map
*
* @param partnerKey API KEY
* @param signType {@link SignType} 签名类型
* @return 构建签名后的 Map
*/
public Map<String, String> createSign(String partnerKey, SignType signType) {
return createSign(partnerKey, signType, true);
}
/**
* 构建签名 Map
*
* @param partnerKey API KEY
* @param signType {@link SignType} 签名类型
* @param haveSignType 签名是否包含 sign_type 字段
* @return 构建签名后的 Map
*/
public Map<String, String> createSign(String partnerKey, SignType signType, boolean haveSignType) {
return WxPayKit.buildSign(toMap(), partnerKey, signType, haveSignType);
}
/**
* 获取属性名数组
*
* @param obj 对象
* @return 返回对象属性名数组
*/
public String[] getFiledNames(Object obj) {
Field[] fields = obj.getClass().getDeclaredFields();
String[] fieldNames = new String[fields.length];
for (int i = 0; i < fields.length; i++) {
fieldNames[i] = fields[i].getName();
}
return fieldNames;
}
/**
* 根据属性名获取属性值
*
* @param fieldName 属性名称
* @param obj 对象
* @return 返回对应属性的值
*/
public Object getFieldValueByName(String fieldName, Object obj) {
try {
String firstLetter = fieldName.substring(0, 1).toUpperCase();
String getter = new StringBuffer().append("get")
.append(firstLetter)
.append(fieldName.substring(1))
.toString();
Method method = obj.getClass().getMethod(getter);
return method.invoke(obj);
} catch (Exception e) {
return null;
}
}
}

View File

@ -0,0 +1,35 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付-下单对账单</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@AllArgsConstructor
@Getter
@Setter
public class BillDownloadModel extends BaseModel{
private String service;
private String version;
private String charset;
private String bill_date;
private String bill_type;
private String sign_type;
private String mch_id;
private String nonce_str;
private String sign;
}

View File

@ -0,0 +1,36 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付-关闭订单</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@AllArgsConstructor
@Getter
@Setter
public class CloseOrderModel extends BaseModel{
private String service;
private String version;
private String charset;
private String sign_type;
private String mch_id;
private String out_trade_no;
private String nonce_str;
private String sign;
private String sign_agentno;
private String groupno;
}

View File

@ -0,0 +1,51 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付-付款码支付</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@AllArgsConstructor
@Getter
@Setter
public class MicroPayModel extends BaseModel {
private String service;
private String version;
private String charset;
private String sign_type;
private String mch_id;
private String out_trade_no;
private String device_info;
private String body;
private String goods_detail;
private String sub_appid;
private String attach;
private String need_receipt;
private String total_fee;
private String mch_create_ip;
private String auth_code;
private String time_start;
private String time_expire;
private String op_user_id;
private String op_shop_id;
private String op_device_id;
private String goods_tag;
private String nonce_str;
private String sign;
private String sign_agentno;
private String groupno;
}

View File

@ -0,0 +1,37 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付-订单查询</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@AllArgsConstructor
@Getter
@Setter
public class OrderQueryModel extends BaseModel {
private String service;
private String version;
private String charset;
private String sign_type;
private String mch_id;
private String out_trade_no;
private String transaction_id;
private String sign_agentno;
private String groupno;
private String nonce_str;
private String sign;
}

View File

@ -0,0 +1,43 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付-退款</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@AllArgsConstructor
@Getter
@Setter
public class RefundModel extends BaseModel{
private String service;
private String version;
private String charset;
private String sign_type;
private String mch_id;
private String out_trade_no;
private String transaction_id;
private String out_refund_no;
private String total_fee;
private String refund_fee;
private String op_user_id;
private String refund_channel;
private String nonce_str;
private String sign;
private String sign_agentno;
private String groupno;
}

View File

@ -0,0 +1,39 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付-查询退款</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@AllArgsConstructor
@Getter
@Setter
public class RefundQueryModel extends BaseModel{
private String service;
private String version;
private String charset;
private String sign_type;
private String mch_id;
private String out_trade_no;
private String transaction_id;
private String out_refund_no;
private String refund_id;
private String nonce_str;
private String sign;
private String sign_agentno;
private String groupno;
}

View File

@ -0,0 +1,36 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付-撤销订单</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@AllArgsConstructor
@Getter
@Setter
public class ReverseModel extends BaseModel{
private String service;
private String version;
private String charset;
private String sign_type;
private String mch_id;
private String out_trade_no;
private String nonce_str;
private String sign;
private String sign_agentno;
private String groupno;
}

View File

@ -0,0 +1,60 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付-统一下单</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@AllArgsConstructor
@Getter
@Setter
public class UnifiedOrderModel extends BaseModel {
private String service;
private String version;
private String charset;
private String sign_type;
private String mch_id;
private String appid;
private String is_raw;
private String is_minipg;
private String out_trade_no;
private String device_info;
private String op_shop_id;
private String body;
private String sub_openid;
private String user_id;
private String attach;
private String sub_appid;
private String total_fee;
private String need_receipt;
private String customer_ip;
private String mch_create_ip;
private String notify_url;
private String time_start;
private String time_expire;
private String qr_code_timeout_express;
private String op_user_id;
private String goods_tag;
private String product_id;
private String nonce_str;
private String buyer_logon_id;
private String buyer_id;
private String limit_credit_pay;
private String sign;
private String sign_agentno;
private String groupno;
}

View File

@ -0,0 +1,37 @@
/**
* <p>IJPay 让支付触手可及封装了微信支付支付宝支付银联支付常用的支付方式以及各种常用的接口</p>
*
* <p>不依赖任何第三方 mvc 框架仅仅作为工具使用简单快速完成支付模块的开发可轻松嵌入到任何系统里 </p>
*
* <p>IJPay 交流群: 723992875</p>
*
* <p>Node.js : https://gitee.com/javen205/TNWX</p>
*
* <p>云闪付-银联 JS 支付获取 userId</p>
*
* @author Javen
*/
package cn.lili.modules.payment.kit.plugin.unionpay.model;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@AllArgsConstructor
@Getter
@Setter
public class UnionPayUserIdModel extends BaseModel{
private String service;
private String version;
private String charset;
private String sign_type;
private String mch_id;
private String nonce_str;
private String sign;
private String user_auth_code;
private String app_up_identifier;
private String sign_agentno;
private String groupno;
}

View File

@ -42,6 +42,7 @@ import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.payment.WechatPaymentSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import cn.lili.modules.wallet.entity.dos.MemberWithdrawApply;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -52,6 +53,8 @@ import javax.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -465,6 +468,63 @@ public class WechatPlugin implements Payment {
}
}
/**
* 微信提现
* 文档地址https://pay.weixin.qq.com/docs/merchant/apis/batch-transfer-to-balance/transfer-batch/initiate-batch-transfer.html
*
* @param memberWithdrawApply 会员提现申请
*/
@Override
public void transfer(MemberWithdrawApply memberWithdrawApply) {
try {
WechatPaymentSetting setting = wechatPaymentSetting();
Connect connect = connectService.queryConnect(
ConnectQueryDTO.builder().userId(UserContext.getCurrentUser().getId())
.unionType(ConnectEnum.WECHAT_OPEN_ID.name()).build()
);
//根据自身情况设置AppId,此处我存放的是服务号的APPID下方的openID需要对应此处的APPID配置
TransferModel transferModel = new TransferModel()
.setAppid(setting.getServiceAppId())
.setOut_batch_no(SnowFlake.createStr("T"))
.setBatch_name("用户提现")
.setBatch_remark("用户提现")
.setTotal_amount(CurrencyUtil.fen(memberWithdrawApply.getApplyMoney()))
.setTotal_num(1)
.setTransfer_scene_id("1000");
List<TransferDetailInput> transferDetailListList = new ArrayList<>();
{
TransferDetailInput transferDetailInput = new TransferDetailInput();
transferDetailInput.setOut_detail_no(SnowFlake.createStr("TD"));
transferDetailInput.setTransfer_amount(CurrencyUtil.fen(memberWithdrawApply.getApplyMoney()));
transferDetailInput.setTransfer_remark("用户提现");
transferDetailInput.setOpenid(connect.getUnionId());
// transferDetailInput.setUserName(
// "757b340b45ebef5467rter35gf464344v3542sdf4t6re4tb4f54ty45t4yyry45");
// transferDetailInput.setUserIdCard(
// "8609cb22e1774a50a930e414cc71eca06121bcd266335cda230d24a7886a8d9f");
transferDetailListList.add(transferDetailInput);
}
transferModel.setTransfer_detail_list(transferDetailListList);
PaymentHttpResponse response = WechatApi.v3(
RequestMethodEnums.POST,
WechatDomain.CHINA.toString(),
WechatApiEnum.TRANSFER_BATCHES.toString(),
setting.getMchId(),
setting.getSerialNumber(),
null,
setting.getApiclient_key(),
JSONUtil.toJsonStr(transferModel)
);
log.info("微信提现响应 {}", response);
//根据自身业务进行接下来的任务处理
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 验证结果执行支付回调
*

View File

@ -555,6 +555,11 @@ public enum WechatApiEnum {
* 连锁品牌-删除分账接收方
*/
BRAND_PROFIT_SHARING_RECEIVERS_delete("/v3/brand/profitsharing/receivers/delete"),
/**
* 发起商家转账
*/
TRANSFER_BATCHES("/v3/transfer/batches"),
;
/**

View File

@ -0,0 +1,29 @@
package cn.lili.modules.payment.kit.plugin.wechat.model;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 转账的明细
*
* @author Bulbasaur
*/
@Data
@Accessors(chain = true)
public class TransferDetailInput {
//商户系统内部区分转账批次单下不同转账明细单的唯一标识要求此参数只能由数字大小写字母组成
private String out_detail_no;
//转账金额单位为
private Integer transfer_amount;
//单条转账备注微信用户会收到该备注UTF8编码最多允许32个字符
private String transfer_remark;
//商户appid下某用户的openid
private String openid;
//收款方真实姓名支持标准RSA算法和国密算法公钥由微信侧提供
//明细转账金额<0.3元时不允许填写收款用户姓名
//明细转账金额 >= 2,000元时该笔明细必须填写收款用户姓名
//同一批次转账明细中的姓名字段传入规则需保持一致也即全部填写或全部不填写
//若商户传入收款用户姓名微信支付会校验用户openID与姓名是否一致并提供电子回单
//private String user_name;
}

View File

@ -0,0 +1,31 @@
package cn.lili.modules.payment.kit.plugin.wechat.model;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 提现
* @author Bulbasaur
*/
@Data
@Accessors(chain = true)
public class TransferModel {
//申请商户号的appid或商户号绑定的appid企业号corpid即为此appid
private String appid;
//商户系统内部的商家批次单号要求此参数只能由数字大小写字母组成在商户系统内部唯一
private String out_batch_no;
//该笔批量转账的名称
private String batch_name;
//转账说明UTF8编码最多允许32个字符
private String batch_remark;
//转账金额单位为转账总金额必须与批次内所有明细转账金额之和保持一致否则无法发起转账操作
private Integer total_amount;
//一个转账批次单最多发起一千笔转账转账总笔数必须与批次内所有明细之和保持一致否则无法发起转账操作
private Integer total_num;
//发起批量转账的明细列表最多一千笔
private List<TransferDetailInput> transfer_detail_list;
//必填指定该笔转账使用的转账场景ID
private String transfer_scene_id;
}

View File

@ -56,4 +56,9 @@ public class MemberWithdrawApply extends BaseEntity {
@ApiModelProperty(value = "sn")
private String sn;
@ApiModelProperty(value = "真实姓名")
private String realName;
@ApiModelProperty(value = "第三方平台账号")
private String connectNumber;
}

View File

@ -11,6 +11,8 @@ import cn.lili.common.utils.SnowFlake;
import cn.lili.common.utils.StringUtils;
import cn.lili.modules.member.entity.dos.Member;
import cn.lili.modules.member.service.MemberService;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.payment.kit.CashierSupport;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.WithdrawalSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
@ -77,6 +79,8 @@ public class MemberWalletServiceImpl extends ServiceImpl<MemberWalletMapper, Mem
*/
@Autowired
private MemberWithdrawApplyService memberWithdrawApplyService;
@Autowired
private CashierSupport cashierSupport;
@Override
public MemberWalletVO getMemberWallet(String memberId) {
@ -302,7 +306,8 @@ public class MemberWalletServiceImpl extends ServiceImpl<MemberWalletMapper, Mem
memberWithdrawApply.setInspectTime(new Date());
//保存或者修改零钱提现
this.memberWithdrawApplyService.saveOrUpdate(memberWithdrawApply);
//TODO 调用自动提现接口
//TODO 做成配置项目
cashierSupport.transfer(PaymentMethodEnum.WECHAT,memberWithdrawApply);
boolean result = true;
//如果微信提现失败 则抛出异常 回滚数据
if (!result) {