微信商户号提现到零钱-附源码

1,471 阅读4分钟

本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。

近期有个需求,需要从企业商户号中给用户提现,比如用户看视频,看够一定时间,然后给用户奖励多少钱,钱够一定的数目是可以提现的。本文所写的是微信商户提现到用户微信零钱。

前提:商户号一定要有最少三十天的流水。详情见微信商户提现API

如果直接去看微信的API,然后写接口,因为微信涉及到很多安全问题,比如签名,各种验证,如果没有第三方jar全自己手写很困难,而且容易出错。

本文找的第三方写好的jar,开发者只需要关注自己的业务,提现逻辑只需要调用个方法就好,很是便捷,基本上半个小时能跑通微信提现到零钱业务。

pom.xml引入源码jar包

<dependency>
    <groupId>com.github.binarywang</groupId>
    <artifactId>weixin-java-pay</artifactId>
    <version>4.1.0</version>
</dependency>

配置文件

wx:
  pay:
    #公众号appid需要用户登录的微信服务号应用的APPID
    appId: 111111
    #商户号
    mchId: 123456
    #商户密钥
    mchKey: 123456789
    #p12证书文件的绝对路径或者以classpath:开头的类路径
    keyPath: classpath:test.p12

mchId和mchKey在商户后台都能拿到,p12证书在商户后台去下载,下载的文件放到resources下面就是以classpath:开头的类路径

初始化配置文件

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
 * <pre>
 *  微信支付属性配置类
 * Created by Binary Wang on 2019/4/17.
 * </pre>
 *
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 */
@Data
@ConfigurationProperties(prefix = "wx.pay")
public class WxPayProperties {
  /**
   * 设置微信公众号或者小程序等的appid.
   */
  private String appId;


  /**
   * 微信支付商户号.
   */
  private String mchId;


  /**
   * 微信支付商户密钥.
   */
  private String mchKey;


  /**
   * 服务商模式下的子商户公众账号ID,普通模式请不要配置,请在配置文件中将对应项删除.
   */
  private String subAppId;


  /**
   * 服务商模式下的子商户号,普通模式请不要配置,最好是请在配置文件中将对应项删除.
   */
  private String subMchId;


  /**
   * apiclient_cert.p12文件的绝对路径,或者如果放在项目中,请以classpath:开头指定.
   */
  private String keyPath;


  /**
   * 微信支付分serviceId
   */
  private String serviceId;


  /**
   * 证书序列号
   */
  private String certSerialNo;


  /**
   * apiV3秘钥
   */
  private String apiv3Key;


  /**
   * 微信支付分回调地址
   */
  private String payScoreNotifyUrl;


  /**
   * apiv3 商户apiclient_key.pem
   */
  private String privateKeyPath;


  /**
   * apiv3 商户apiclient_cert.pem
   */
  private String privateCertPath;


}

微信支付自动配置

import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * <pre>
 *  微信支付自动配置
 *  Created by BinaryWang on 2019/4/17.
 * </pre>
 *
 * @author <a href="https://github.com/binarywang">Binary Wang</a>
 */
@Configuration
@EnableConfigurationProperties(WxPayProperties.class)
@ConditionalOnClass(WxPayService.class)
@ConditionalOnProperty(prefix = "wx.pay", value = "enabled", matchIfMissing = true)
public class WxPayAutoConfiguration {
  private WxPayProperties properties;


  @Autowired
  public WxPayAutoConfiguration(WxPayProperties properties) {
    this.properties = properties;
  }


  /**
   * 构造微信支付服务对象.
   *
   * @return 微信支付service
   */
  @Bean
  @ConditionalOnMissingBean(WxPayService.class)
  public WxPayService wxPayService() {
    final WxPayServiceImpl wxPayService = new WxPayServiceImpl();
    WxPayConfig payConfig = new WxPayConfig();
    payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
    payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
    payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey()));
    payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
    payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
    payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));
    //以下是apiv3以及支付分相关
    payConfig.setServiceId(StringUtils.trimToNull(this.properties.getServiceId()));
    payConfig.setPayScoreNotifyUrl(StringUtils.trimToNull(this.properties.getPayScoreNotifyUrl()));
    payConfig.setPrivateKeyPath(StringUtils.trimToNull(this.properties.getPrivateKeyPath()));
    payConfig.setPrivateCertPath(StringUtils.trimToNull(this.properties.getPrivateCertPath()));
    payConfig.setCertSerialNo(StringUtils.trimToNull(this.properties.getCertSerialNo()));
    payConfig.setApiV3Key(StringUtils.trimToNull(this.properties.getApiv3Key()));


    wxPayService.setConfig(payConfig);
    return wxPayService;
  }
}

主要的提现逻辑

EntPayRequest entPayRequest = new EntPayRequest();
//商户ID
entPayRequest.setMchId(wxPayService.getConfig().getMchId());
//从配置读的用户登录的微信服务号应用,如果是多个服务号从自己数据库读取,本文只针对一个服务号提现一个应用appid
entPayRequest.setMchAppid(wxPayService.getConfig().getAppId());
//提现到哪个微信服务号关注人的openid
entPayRequest.setOpenid("openid");
//当前提现的订单号-自己创建
entPayRequest.setNonceStr("oooopppxxx");
//保证唯一性幂等性的ID自己创建
entPayRequest.setPartnerTradeNo("xxxxx");
entPayRequest.setCheckName("no_check");
//提现金额单位分
entPayRequest.setAmount(100);
//Ip地址 非必传字段
entPayRequest.setSpbillCreateIp(InetAddress.getLocalHost().getHostAddress());
entPayRequest.setDescription("游戏提现标题");
//调用提现API
EntPayResult entPayResult= wxPayService.getEntPayService().entPay(entPayRequest);

注意:提现的现金单位是分,其他主要参数对照微信官方提现到零钱的API接口。

总结

引入第三方支付jar,配置商户信息,初始化微信支付的两个配置类,然后是提现支付的业务逻辑。

如果是工业级别的提现,因为涉及到现金,好的建议是提现申请和提现到用户零钱异步化。

在提现到零钱的业务中做验证策略,防止被刷,提现到零钱的业务需要做到随身可以停止,当然商户后台也有对用户每天最多提现的次数,和最大提现金额的限制。

本文只是简单的跑通了提现的主要流程,涉及到安全和核心开发,需要自己去做安全的管控。