微信零钱支付Java demo

  1. 已开通微信应用(服务号或者小程序),主要用到APPID+OPENID
  2. 已开通微信商户,并且开通企业付款到零钱,主要用到商户号+商户秘钥+商户证书
    在这里偷个懒,具体怎么配置就不贴出来啦,大家可以去官网看一下,配置好之后保存好p12支付证书。
    首先在pom.xml文件内引入所需要得包
<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 写自己的逻辑

你可能感兴趣的:(笔记,java)