一、简介
微信作为现在人民的使用,并且微信成为众多商家做活动的首选项,基于微信以前企业微信付款到零钱改为了企业微信商家转账到零钱。因为开发需求,亲自实现了企业微信商家转账到零钱的功能。
二、微信企业商家转账到零钱开通
开通条件:
1,商户号需入驻90天;
2,需要有连续30天的交易流水(截止今日回推30天)
流程:
找到产品中心,点击开通商家转账到零钱功能。
额度限制:
跟以前微信企业支付到零钱一样,目前商户号企业转账功能开通后,初始额度统一为单用户单日单笔200元,单日总额度10W元,如果是初始商家账户,一个用户每日只能转账10次。
默认情况下,企业付款到零钱使用商户号基本户(或余额账户)余额。如商户号已开通运营账户,则企业付款到零钱使用运营账户内的资金。
三、实现商家转账到零钱(该功能需要跟微信公众号一起)
微信公众号和商户号需要关联,可以在公众号查看是否关联。如图:
四、商户号实现转账方式
五、 Java代码实现案例
1、首先引入封装好的jar包
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-pay-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
2、yml里面支付配置
wx:
# 微信公众号
mp:
app-id: 微信公众号appid
secret: 微信公众号密钥
token: arecatoken
# 微信企业支付
pay:
appId: 微信公众号appid
mchId: 微信企业商户号
apiv3-key: api3key密钥
private-key-path: 证书apiclient_key
private-cert-path: 证书apiclient_cert
3、具体Java代码如下
controller
代码:
public class WxEntPayController {
@Autowired
private WxEntPayService wxEntPayService;
/**
* 支付接口
*
* @param entPayReq
* @return
*/
@AutoLog(value = "支付接口")
@ApiOperation(value = "支付接口", notes = "支付接口")
@PostMapping(value = "/entPay")
public Result<?> entPay(@RequestBody EntPayReq entPayReq) {
wxEntPayService.entPay(entPayReq);
return Result.OK("支付成功!");
}
}
EntPayReq 实体类:
@ApiModel("企业支付请求")
@Data
public class EntPayReq {
/**
* <pre>
* 字段名:金额.
* 变量名:amount
* 是否必填:是
* 示例值:10099
* 类型:int
* 描述:企业付款金额, 单位为分
* </pre>
*/
@NotNull(message = "金额不能为空")
@Min(value = 30, message = "最小金额30分")
@ApiModelProperty(value = "金额(单位分)", required = true)
private Integer amount;
/**
* <pre>
* 字段名:校验用户姓名选项.
* 变量名:check_name
* 是否必填:是
* 示例值:OPTION_CHECK
* 类型:String
* 描述:NO_CHECK:不校验真实姓名
* FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账)
* OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)
* </pre>
*/
@ApiModelProperty(value = "校验用户姓名选项 [NO_CHECK:不校验真实姓名,FORCE_CHECK:强校验真实姓名(未实名认证的用户会校验失败,无法转账),OPTION_CHECK:针对已实名认证的用户才校验真实姓名(未实名认证用户不校验,可以转账成功)]")
private String checkName;
/**
* <pre>
* 字段名:收款用户姓名.
* 变量名:re_user_name
* 是否必填:可选
* 示例值:马花花
* 类型:String
* 描述:收款用户真实姓名。
* 如果check_name设置为FORCE_CHECK或OPTION_CHECK, 则必填用户真实姓名
* </pre>
*/
@ApiModelProperty("收款用户真实姓名")
private String reUserName;
/**
* <pre>
* 字段名:设备号.
* 变量名:device_info
* 是否必填:否
* 示例值:13467007045764
* 类型:String(32)
* 描述:微信支付分配的终端设备号
* </pre>
*/
@ApiModelProperty("设备号")
private String deviceInfo;
/**
* <pre>
* 字段名:Ip地址.
* 变量名:spbill_create_ip
* 是否必填:是
* 示例值:192.168.0.1
* 类型:String(32)
* 描述:调用接口的机器Ip地址
* </pre>
*/
@NotBlank(message = "不能为空")
@ApiModelProperty(value = "Ip地址", required = true)
private String spbillCreateIp;
/**
* 支付订单号
*/
@ApiModelProperty(value = "支付订单号[非必填]")
private String partnerTradeNo;
/**
* 批次订单号
*/
@ApiModelProperty(value = "批次订单号")
private String batchTradeNo;
}
业务层代码:
public interface WxEntPayService {
/**
* 微信支付
*
* @param entPayReq
*/
void entPay(EntPayReq entPayReq);
}
//业务实现代码
@Service
@Slf4j
public class WxEntPayServiceImpl implements WxEntPayService {
@Autowired
private WxPayService wxPayService;
/**
* 不校验真实姓名.
*/
public static final String NO_CHECK = "NO_CHECK";
/**
* 微信支付
*
* @param req
*/
@Override
public void entPay(EntPayReq req) {
log.info("开始企业支付到零钱 [req:{}]",req);
req.setCheckName(StringUtils.isBlank(req.getCheckName()) ? NO_CHECK : req.getCheckName());
//商户订单号
String partnerTradeNo = StringUtils.isNotBlank(req.getPartnerTradeNo()) ? req.getPartnerTradeNo() : RandomUtil.randomInt(20);
req.setPartnerTradeNo(partnerTradeNo);
//随机获取批次号 切记仅用于测试
String s = RandomNumber.GetRandom();
req.setBatchTradeNo(s);
TransferService transferService = wxPayService.getTransferService();
//获取appId
String appId = wxPayService.getConfig().getAppId();
//创建批次对象
TransferBatchesRequest transferBatchesRequest=new TransferBatchesRequest();
transferBatchesRequest.setAppid(appId);
//设置批次名称 可不写
transferBatchesRequest.setBatchName("测试批次");
//设置批次备注 可不写
transferBatchesRequest.setBatchRemark("测试");
//设置该批次编号
transferBatchesRequest.setOutBatchNo(s);
//设置该批次总个数
transferBatchesRequest.setTotalNum(1);
//设置该批次总金额
transferBatchesRequest.setTotalAmount(req.getAmount());
//创建收款人请求对象
ArrayList<TransferBatchesRequest.TransferDetail> transferDetails = new ArrayList<>();
TransferBatchesRequest.TransferDetail transferDetail=new TransferBatchesRequest.TransferDetail();
//转账的编号
transferDetail.setOutDetailNo(req.getPartnerTradeNo());
//转账的金额
transferDetail.setTransferAmount(req.getAmount());
//转账的注释
transferDetail.setTransferRemark("转账金额");
//以实际微信公众号那边的openid为准
transferDetail.setOpenid("获取到用户的openid");
//把收款人对象放到批次里面
transferDetails.add(transferDetail);
transferBatchesRequest.setTransferDetailList(transferDetails);
TransferBatchesResult transferBatchesResult=null;
try {
transferBatchesResult = transferService.transferBatches(transferBatchesRequest);
log.info("企业支付完成:[msg:{}]",transferBatchesResult);
} catch (WxPayException e) {
e.printStackTrace();
log.info("企业支付失败:[msg:{}]",e.getMessage());
}
}
工具类代码:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;
public class RandomNumber {
// 使用单例模式,不允许直接创建实例
private RandomNumber() {
}
// 创建一个空实例对象,类需要用的时候才赋值
private static RandomNumber instance = null;
// 单例模式--懒汉模式
public static synchronized RandomNumber getInstance() {
if (instance == null) {
instance = new RandomNumber();
}
return instance;
}
// 全局自增数
private static int count = 1;
// 格式化的时间字符串
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
// 获取当前时间年月日时分秒毫秒字符串
private static String getNowDateStr() {
return sdf.format(new Date());
}
// 记录上一次的时间,用来判断是否需要递增全局数
private static String now = null;
//定义锁对象
private final static ReentrantLock lock = new ReentrantLock();
//调用的方法
public static String GetRandom() {
String Newnumber = null;
String dateStr = getNowDateStr();
//加锁
lock.lock();
//判断是时间是否相同
if (dateStr.equals(now)) {
try {
if (count >= 10000) {
count = 1;
}
if (count < 10) {
Newnumber = "N" + getNowDateStr() + "000" + count;
} else if (count < 100) {
Newnumber = "N" + getNowDateStr() + "00" + count;
} else if (count < 1000) {
Newnumber = "N" + getNowDateStr() + "0" + count;
} else {
Newnumber = "N" + getNowDateStr() + count;
}
count++;
} catch (Exception e) {
} finally {
lock.unlock();
}
} else {
count = 1;
now = getNowDateStr();
try {
if (count >= 10000) {
count = 1;
}
if (count < 10) {
Newnumber = "N" + getNowDateStr() + "000" + count;
} else if (count < 100) {
Newnumber = "N" + getNowDateStr() + "00" + count;
} else if (count < 1000) {
Newnumber = "N" + getNowDateStr() + "0" + count;
} else {
Newnumber = "N" + getNowDateStr() + count;
}
count++;
} catch (Exception e) {
} finally {
lock.unlock();
}
}
return Newnumber;//返回的值
}
}
这样就可以实现商家转账到零钱(单个人),该方法仅用于测试。
批量转账到零钱是为了实现一批次转账到多个账户的方法,这里我只用了一个用户转账
异常处理需要自己去写,暂时没有测试完全。