<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.8.9</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>fluent-hc</artifactId>
<version>4.5.3</version>
</dependency>
然后在.yml 配置文件中配置证书路径
paywechat:
keyUserPath: E:\opt\appkey\apiclient_cert.p12
说明:我这里是配置的本地用于测试的,正式环境请使用线上地址
具体代码如下
@Slf4j
@Configuration
@ConfigurationProperties("paywechat")
public class PayConfig {
private String keyUserPath;
public String getKeyUserPath() {
return keyUserPath;
}
public void setKeyUserPath(String keyUserPath) {
this.keyUserPath = keyUserPath;
}
/**
* 付款到零钱
* @return user star
*/
@PostConstruct
public Map<String,byte[]> init() {
Map map = new HashMap(2);
byte[] user = null;
if (!Files.exists(Paths.get(keyUserPath))) {
log.error("用户商户证书不存在,路径:{}", keyUserPath);
}
try {
user = Files.readAllBytes(Paths.get(keyUserPath));
log.debug("用户商户证书成功");
} catch (Exception e) {
log.error("用户证书失败", e);
}
map.put("user",user);
return map;
}
}
import com.bwc.admin.util.GxCommon;
import com.bwc.admin.util.WxApiUtil;
import com.bwc.admin.util.WxPayUtil;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.HttpClient;
import org.apache.http.client.fluent.Request;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.stereotype.Service;
import javax.net.ssl.SSLContext;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import org.apache.http.client.fluent.Executor;
/**
* @Author more_Joy
* @Date 2021/2/19 11:57
* @Version 1.0
*/
@Slf4j
@Service
public class WeChatEnterprisePayService {
/**
* 创建订单-普通商户
*
* @param appId 小程序标识
* @param mchId 商户号
* @param partnerTradeNo 商户订单号
* @param openId 用户openid
* @param checkName 是否实名认证
* @param reUserName 收款方姓名
* @param amount 金额
* @param desc 描述信息
* @param spbillCreateIp 终端IP
* @param apiKey 商户支付密钥
* @param cert 支付证书
* @return 业务消息
*/
public Map<String, Object> pay(String appId, String mchId, String partnerTradeNo, String openId, Boolean checkName, String reUserName,
Integer amount, String desc, String spbillCreateIp, String apiKey, byte[] cert) {
log.debug("创建订单-普通商户, appId = [" + appId + "], mchId = [" + mchId + "], partnerTradeNo = [" + partnerTradeNo + "], openId = [" + openId + "], checkName = [" + checkName + "], reUserName = [" + reUserName + "], amount = [" + amount + "], desc = [" + desc + "], spbillCreateIp = [" + spbillCreateIp + "], apiKey = [" + apiKey + "], cert = [" + cert + "]");
Map<String, Object> data = new HashMap<>();
try {
TreeMap<String, Object> params = new TreeMap<>();
// 公众账号appid
// mch_appid
// 是
// wx8888888888888888
// String
// 微信分配的公众账号ID(企业号corpid即为此appId)
params.put("mch_appid", appId);
// 商户号
// mchid
// 是
// 1900000109
// String(32)
// 微信支付分配的商户号
params.put("mchid", mchId);
// 设备号
// device_info
// 否
// String(32)
// 013467007045764
// 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"
params.put("device_info", "WEB");
// 随机字符串
// nonce_str
// 是
// String(32)
// 5K8264ILTKCH16CQ2502SI8ZNMTM67VS
// 随机字符串,不长于32位。推荐随机数生成算法
// https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=4_3
params.put("nonce_str", WxPayUtil.getRandomUpperStringByLength(32));
// 商户订单号
// partner_trade_no
// 是
// 10000098201411111234567890
// String
// 商户订单号,需保持唯一性 (只能是字母或者数字,不能包含有符号)
params.put("partner_trade_no", partnerTradeNo);
// 用户openid
// openid
// 是
// oxTWIuGaIt6gTKsQRLau2M0yL16E String
// 商户appid下,某用户的openid
params.put("openid", openId);
// 校验用户姓名选项
// check_name
// 是
// FORCE_CHECK
// String
// NO_CHECK:不校验真实姓名 FORCE_CHECK:强校验真实姓名
params.put("check_name", checkName ? "FORCE_CHECK" : "NO_CHECK");
// 收款用户姓名
// re_user_name
// 可选
// 马花花
// String
// 收款用户真实姓名。如果check_name设置为FORCE_CHECK,则必填用户真实姓名
if (checkName) {
params.put("re_user_name", reUserName);
}
// 金额
// amount
// 是
// 10099
// int
// 企业付款金额,单位为分
params.put("amount", amount);
// 企业付款描述信息
// desc
// 是
// 理赔
// String
// 企业付款操作说明信息。必填。
params.put("desc", desc);
// Ip地址
// spbill_create_ip
// 是
// 192.168.0.1
// String(32)
// 调用接口的机器Ip地址
params.put("spbill_create_ip", spbillCreateIp);
//
String sign = WxPayUtil.createSign(params, apiKey);
//检测签名结果
if (StringUtils.isNotBlank(sign)) {
log.debug("签名字符串:{}", sign);
params.put("sign", sign);
XmlMapper xmlMapper = new XmlMapper();
ObjectWriter writer = xmlMapper.writer().withRootName("xml");
byte[] postData = writer.writeValueAsBytes(params);
log.debug("提交参数:{}", new String(postData, StandardCharsets.UTF_8));
//指定读取证书格式为PKCS12
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//读取本机存放的PKCS12证书文件
try (ByteArrayInputStream instream = new ByteArrayInputStream(cert)) {
//指定PKCS12的密码(商户ID)
keyStore.load(instream, mchId.toCharArray());
SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, mchId.toCharArray()).build();
//指定TLS版本
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext);
//设置httpclient的SSLSocketFactory
HttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.build();
Executor executor = Executor.newInstance(httpclient);
String result = executor.execute(Request.Post(WxApiUtil.WX_ENTERPRISE_PAY_URL)
.bodyByteArray(postData, ContentType.APPLICATION_XML))
.returnContent()
.asString(StandardCharsets.UTF_8);
log.debug("响应结果:{}", result);
//处理返回结果
String temp = result.replaceAll(", "").replaceAll("]]>", "");
log.debug("响应结果: {}", temp);
data = xmlMapper.readValue(temp, new TypeReference<Map<String, String>>() {});
if (data.containsKey("payment_no")) {
// 设置ApiKey
params.put(GxCommon.APIKEY, apiKey);
// 设置请求包
data.put("request_tag", xmlMapper.writeValueAsString(params));
// 设置响应包
data.put("return_tag", temp);
} else {
log.error("创建订单失败", temp);
}
} catch (Exception e) {
log.error("读取证书失败", e);
}
}
} catch (Exception e) {
log.error("签名失败", e);
}
return data;
}
}
public class Common {
public static final String CHARSET = "UTF-8";
//测试URL
// public static final String URL = "http://116.228.235.114:38080/External_Connect/newWechats/newWeChatpayment_mobile.action";
//线上URL
public static final String URL = "http://extman.kefupay.cn/newWechats/newWeChatpayment_mobile.action";
// 商户标识(根据分配的用户标识进行修改)
// 改为从配置文件读取
// public static final String userid = "995418";
// public static final String key = "edb73bc10b8329e1b1c8b780c3f557b5";
/**
* 交易类型---------BEGIN
*/
public static final String newRegister = "hf_newRegister";//注册商户接口
public static final String newDownLoadKey = "hf_newDownLoadKey";//下载密钥接口
public static final String newVerifyInfo = "hf_newVerifyInfo";//验卡接口
public static final String newWeixinPay = "hf_newWeixinPay";//二维码支付接口
public static final String newOrderConfirm = "hf_newOrderConfirm";//订单状态查询接口
public static final String newChangeRate = "hf_newChangeRate";//同步商户签约费率
public static final String newGetACodePay = "hf_newGetACodePay";//公众号支付接口
public static final String newALiPay = "hf_newALiPay";//二维码支付宝支付接口
/**
* 易试扫码支付
*/
public static final String ys_ALiPay = "ys_ALiPay";//易试支付宝扫码支付
public static final String ys_WXPay = "ys_WXPay";//易试微信支付
public static final String ys_WeChatPay = "ys_WeChatPay";//易试微信公众号支付
public static final String ys_Withdraw = "ys_Withdraw";//易试代付
public static final String ys_WithdrawCheck = "ys_WithdrawCheck";//易试代付查询
}
public interface GxCommon {
String PAY_LOG_KEY_PREFIX = "pay-log:";
String PAY_LOG_PROCESS_KEY_PREFIX = "pay-log-process:";
// 订单支付日志队列标识
// String PAY_LOG_QUEUE = "pay-log-queue";
String PROPERTY_PAY_LOG_QUEUE = "spring.rabbitmq.pay-log-queue";
String MACHID = "machid";
String APIKEY = "apikey";
String KEY_CODE_BG = "code_bg";
String KEY_SMS_SIGN = "sms_sign";
// String ORDER_AFTER ="__wsc" ;
String KEY_KEY = "sdfjkfki766837323";
String THIRD_REQUEST = "third_request";
String REQUEST = "request";
String CREATE_ORDER_LIMIT ="下单过于频繁" ;
String APPLET_LOGO = "applet_logo";
String APPLET_QR_BG ="applet_qr_bg" ;
String EXPORT_LITMIT = "请选择起止时间导出,并且间隔不要超过一天,或者选择一用户进行导出";
String EXPORT_NO_CONTENT ="没有数据可导出" ;
String EXPORT_LITMIT_CUSTOMER ="请选择起止时间导出,并且间隔不要超过一天" ;
}
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Map;
import java.util.Random;
/**
* @Author more_Joy
* @Date 2021/2/19 14:06
* @Version 1.0
*/
@Slf4j
public class GxlUtil {
public static Integer now() {
return (int) LocalDateTime.now().atOffset(ZoneOffset.ofHours(8)).toEpochSecond();
}
public static String createUniontid() {
//Random random = new Random();
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < 1; i++) {
// sb.append(random.nextInt(9));
// }
// 订单号
return LocalDateTime.now().atOffset(ZoneOffset.ofHours(8)).format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")) + new Random().nextInt(9);
}
public static boolean signCheck(Map paramMap, String key, String sign) {
String signStr = MD5Utils.getSignParam(paramMap);
log.debug("计算签名的报文为:{}", sign);
signStr = MD5Utils.getKeyedDigest(signStr, key);
return sign!=null &&sign.equals(signStr);
}
public static String getLogoUrl(String payconfig) {
JSONObject appconfig = JSON.parseObject(payconfig);
if (appconfig.containsKey("other")) {
JSONObject other = appconfig.getJSONObject("other");
return other.getString(GxCommon.APPLET_LOGO);
}
return null;
}
public static String getQrbgUrl(String payconfig) {
JSONObject appconfig = JSON.parseObject(payconfig);
if (appconfig.containsKey("other")) {
JSONObject other = appconfig.getJSONObject("other");
return other.getString(GxCommon.APPLET_QR_BG);
}
return null;
}
public static void wreteResponse(HttpServletResponse response, String msg) {
response.setContentType("text/html; charset=utf-8");
try(ServletOutputStream out = response.getOutputStream();){
out.write(msg.getBytes("utf-8"));
out.flush();
}catch (Exception e){
}
}
}
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* @Author more_Joy
* @Date 2021/2/19 15:15
* @Version 1.0
*/
public class MD5Utils {
public static String getKeyedDigest(String strSrc, String key) {
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
md5.update(strSrc.getBytes(Common.CHARSET));
String result = "";
byte[] temp;
temp = md5.digest(key.getBytes(Common.CHARSET));
for (int i = 0; i < temp.length; i++) {
result += Integer.toHexString((0x000000ff & temp[i]) | 0xffffff00).substring(6);
}
return result;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static String getSignParam(Map<String, String> params) {
StringBuilder buf = new StringBuilder((params.size() + 1) * 10);
buildPayParams(buf, params, false);
String result = buf.toString();
return result;
}
/**
* @param payParams
* @return
* @author
*/
public static void buildPayParams(StringBuilder sb, Map<String, String> payParams, boolean encoding) {
List<String> keys = new ArrayList<String>(payParams.keySet());
Collections.sort(keys);
for (String key : keys) {
sb.append(key).append("=");
if (encoding) {
sb.append(urlEncode(payParams.get(key)));
} else {
sb.append(payParams.get(key));
}
sb.append("&");
}
sb.setLength(sb.length() - 1);
} /**
* @param payParams
* @return
* @author
*/
public static void buildPayParamsDesc(StringBuilder sb, Map<String, String> payParams, boolean encoding) {
List<String> keys = new ArrayList<String>(payParams.keySet());
Collections.sort(keys);
Collections.reverse(keys);
for (String key : keys) {
sb.append(key).append("=");
if (encoding) {
sb.append(urlEncode(payParams.get(key)));
} else {
sb.append(payParams.get(key));
}
sb.append("&");
}
sb.setLength(sb.length() - 1);
}
public static String urlEncode(String str) {
try {
return URLEncoder.encode(str, "UTF-8");
} catch (Throwable e) {
return str;
}
}
/**
* 将指定的字符串用MD5加密 originstr 需要加密的字符串
*
* @param originstr
* @return
*/
public static String ecodeByMD5(String originstr) {
String result = null;
char[] hexDigits =
{// 用来将字节转换成 16 进制表示的字符
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
if (originstr != null) {
try {
// 返回实现指定摘要算法的 MessageDigest 对象
MessageDigest md = MessageDigest.getInstance("MD5");
// 使用utf-8编码将originstr字符串编码并保存到source字节数组
byte[] source = originstr.getBytes("utf-8");
// 使用指定的 byte 数组更新摘要
md.update(source);
// 通过执行诸如填充之类的最终操作完成哈希计算,结果是一个128位的长整数
byte[] tmp = md.digest();
// 用16进制数表示需要32位
char[] str = new char[32];
for (int i = 0, j = 0; i < 16; i++) {
// j表示转换结果中对应的字符位置
// 从第一个字节开始,对 MD5 的每一个字节
// 转换成 16 进制字符
byte b = tmp[i];
// 取字节中高 4 位的数字转换
// 无符号右移运算符>>> ,它总是在左边补0
// 0x代表它后面的是十六进制的数字. f转换成十进制就是15
str[j++] = hexDigits[b >>> 4 & 0xf];
// 取字节中低 4 位的数字转换
str[j++] = hexDigits[b & 0xf];
}
result = new String(str);// 结果转换成字符串用于返回
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return result;
}
/**
* 创建密匙
*
* @param algorithm 加密算法,可用 DES,DESede,Blowfish
* @return SecretKey 秘密(对称)密钥
*/
public static SecretKey createSecretKey(String algorithm) {
// 声明KeyGenerator对象
KeyGenerator keygen;
// 声明 密钥对象
SecretKey deskey = null;
try {
// 返回生成指定算法的秘密密钥的 KeyGenerator 对象
keygen = KeyGenerator.getInstance(algorithm);
// 生成一个密钥
deskey = keygen.generateKey();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
// 返回密匙
return deskey;
}
public static String getSignParamDesc(Map params) {
StringBuilder buf = new StringBuilder((params.size() + 1) * 10);
buildPayParamsDesc(buf, params, false);
String result = buf.toString();
return result;
}
}
public class WxApiUtil {
// 小程序二维码-限制数量
public static final String WX_QR_CODE_LIMIT_URL = "https://api.weixin.qq.com/cgi-bin/wxaapp/createwxaqrcode";
// 小程序码-限制数量
public static final String WE_CHAT_QR_CODE_LIMIT_URL = "https://api.weixin.qq.com/wxa/getwxacode";
// 小程序码-不限数量
public static final String WE_CHAT_QR_CODE_UN_LIMIT_URL = "http://api.weixin.qq.com/wxa/getwxacodeunlimit";
// 微信Token地址
public static final String WX_ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token";
// 微信预下单地址
public static final String WX_PRE_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
// 微信支付结果检验地址
public static final String WX_VERIFY_PAY_RESULT_URL = "https://api.mch.weixin.qq.com/pay/orderquery";
// 微信企业付款地址
public static final String WX_ENTERPRISE_PAY_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers";
//微信查询企业付款
public static final String WX_PAY_RESULT_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/gettransferinfo";
//微信发送模板消息
public static final String WX_SEND_TEMPLATE = "https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send";
}
import java.util.LinkedList;
import java.util.SortedMap;
import org.springframework.util.StringUtils;
import java.io.UnsupportedEncodingException;
import java.util.Random;
/**
* @Author more_Joy
* @Date 2021/2/19 14:00
* @Version 1.0
*/
public class WxPayUtil {
/**
* 生成签名
*
* @param params
* @return
*/
public static String createSign(SortedMap<String, Object> params, String apiKey, String charset) {
//将map中的参数转换成带有&的字符串
LinkedList<String> paramList = new LinkedList<>();
params.forEach((k, v) -> {
if (null != v && !"".equals(v)) {
paramList.add(k + "=" + v);
}
});
String waitSignStr = String.join("&", paramList);
//拼接API密钥:
String stringSignTemp = waitSignStr + "&key=" + apiKey;
//签名
return MD5Util.MD5Encode(stringSignTemp, charset).toUpperCase();
}
/**
* 生成签名
*
* @param params
* @return
*/
public static String createSign(SortedMap<String, Object> params, String apiKey) {
return createSign(params, apiKey, "UTF-8");
}
/**
* 签名校验
*
* @return
* @throws UnsupportedEncodingException
*/
@SuppressWarnings("rawtypes")
public static boolean verifyPaySign(SortedMap paras, String sign, String appKey, String charset) {
String paySign = WxPayUtil.createSign(paras, appKey, charset);
return !StringUtils.isEmpty(paySign) && paySign.equalsIgnoreCase(sign);
}
/**
* 签名校验
*
* @return
* @throws UnsupportedEncodingException
*/
@SuppressWarnings("rawtypes")
public static boolean verifyPaySign(SortedMap paras, String sign, String appKey) {
return verifyPaySign(paras, sign, appKey, "UTF-8");
}
/**
* 生成指定长度大写随机字符串
*
* @param length 长度
* @return 随机字符串
*/
public static String getRandomUpperStringByLength(int length) {
return getRandomStringByLength(length).toUpperCase();
}
/**
* 生成指定长度小写写随机字符串
*
* @param length 长度
* @return 随机字符串
*/
public static String getRandomLowerStringByLength(int length) {
return getRandomStringByLength(length);
}
private static String getRandomStringByLength(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 返回微信支付回调结果
*
* @param returnCode 代码
* @param returnMsg 消息
* @return 回调结果
*/
public static String generateReturnData(String returnCode, String returnMsg) {
return "" +
" + returnCode + "]]> " +
" + returnMsg + "]]> " +
"";
}
}
import com.bwc.admin.config.PayConfig;
import com.bwc.admin.dao.TransferAccountsLogMapper;
import com.bwc.admin.dao.UserMapper;
import com.bwc.admin.model.TransferAccountsLog;
import com.bwc.admin.model.User;
import com.bwc.admin.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author more_Joy
* @Date 2021/2/19 14:16
* @Version 1.0
*/
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class UserAccountService {
@Autowired
private WeChatEnterprisePayService weChatEnterprisePayService;
@Autowired
private PayConfig payConfig;
@Autowired
private Environment environment;
@Autowired
private UserMapper userMapper;
@Autowired
private TransferAccountsLogMapper transferAccountsLogMapper;
Lock lock = new ReentrantLock();
/**
* 用户提现
* 1、判断用户余额是否是提现金范围内 user_acc
* 2、扣用户的余额
* 2、请求微信付款
* 3、成功之后添加日志 失败个人余额补回去
*
* @return
*/
public int userCash(String clientIp, Long userId, BigDecimal money,Integer type) {
log.debug("进入用户提现接口 , clientIp=[" + clientIp + "],userId = [" + userId + "], money = [" + money + "]");
lock.lock();
try {
User userAccount = userMapper.findById(userId);
//1、判断用户余额够不够 user_acc
String desc="";
BigDecimal totalFee = money.multiply(new BigDecimal("100"));
if (userAccount != null) {
switch (type){
//返现
case 1:
desc = "商家转账 " + totalFee.divide(new BigDecimal("100"), BigDecimal.ROUND_DOWN) + " 元";
break;
case 2:
//提现的时候判断是否符合标准
desc = "提现 " + totalFee.divide(new BigDecimal("100"), BigDecimal.ROUND_DOWN) + " 元";
break;
default:
break;
}
//2、请求微信付款
// 调用微信企业付款
String appid = environment.getProperty("wx.app_id");
String mchId = environment.getProperty("wx.mch_id");
String apikey = environment.getProperty("wx.key");
String uniontid = GxlUtil.createUniontid();
// P12支付证书
// byte[] p12 = new byte[0];
byte[] p12 = payConfig.init().get("user");
//BigDecimal totalFee = money.multiply(new BigDecimal("100"));
//String desc = "商家转账 " + totalFee.divide(new BigDecimal("100"), BigDecimal.ROUND_DOWN) + " 元";
//String clientIp = ClientIPUtil.getClientIP(request);
Map<String, Object> dataMap = weChatEnterprisePayService.pay(appid, mchId, uniontid,
userAccount.getOpenId(), false, userAccount.getNickName(), totalFee.intValue(), desc,
clientIp, apikey, p12);
log.debug("支付结果:{}", dataMap);
//4、成功或者失败添加日志 自己的逻辑
if (!dataMap.containsKey("payment_no")) {
transferAccountsLogMapper.insert(new TransferAccountsLog(){{
setUserId(userId);
setTransferAmount(money);
setStatus(0);
setCreateTime(new Date());
setUpdateTime(new Date());
setCreateBy(SecurityUtils.getUsername());
}});
return EnError.PAYMENT_FAILED.getCode();
} else {
//添加日志
transferAccountsLogMapper.insert(new TransferAccountsLog(){{
setUserId(userId);
setTransferAmount(money);
setStatus(1);
setCreateTime(new Date());
setUpdateTime(new Date());
setCreateBy(SecurityUtils.getUsername());
}});
return 1;
}
}else {
return EnError.NONEMPTY_USER.getCode();
}
} catch (Exception e) {
log.error("支付用户零钱失败,请联系管理员", e.getMessage());
return EnError.PAYMENT_FAILED.getCode();
}finally {
lock.unlock();
}
}
}```
UserAccountService中的代码 大家根据自己的实际情况修改,appid,mchId,apikey 修改成自己的,还有记录日志及查询用户,包括转账成功后的逻辑更改成自己的逻辑,下面再贴一个测试代码
@ApiOperation(value="测试支付")
@GetMapping("test")
public Object test(HttpServletRequest request){
String clientIp = ClientIPUtil.getClientIP(request);
BigDecimal bigDecimal = new BigDecimal(1);
return userAccountService.userCash(clientIp,12L,bigDecimal,1);
}
整体来说总结如下:
1、商户号开通此功能
2、下载p12文件
3、Yml配置文件添加
#付款到余额
paywechat:
keyStarPath: 证书地址
4、PayConfig.java
5、UserAccountService.java 写自己的逻辑