zl程序教程

您现在的位置是:首页 >  APP

当前栏目

java小程序微信支付与提现

2023-02-18 16:41:07 时间

去微信支付开发文档下载官方demo

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1 得到以下几个文件

编写微信常量文件用来存放密钥

/**
 * 微信相关属性
 * @author : LiYu
 * @date : 2020-09-22 16:30
 **/
public final class WxServiceEnvConstants {
    private WxServiceEnvConstants(){}


    /**
     * 公众号 秘钥
     */
    public static final String SECRET_KEY = "*************************";
    /**
     * 公众号 appId
     */
    public static final String APP_ID = "*************************";
    /**
     * 小程序 appId
     */
    public static final String APPLET_APP_ID = "*************************";
    /**
     * 小程序 秘钥
     */
    public static final String APPLET_SECRET_KEY = "*************************";
    /**
     * 商家id
     */
    public static final String MCH_ID = "*************************";
    /**
     * 商家秘钥
     */
    public static final String API_SECRET_KEY = "*************************";
    /**
     * 商家证书地址
     */
    public static final String CERT_PATH = "/cert/1554590811_20190916_cert/apiclient_cert.p12";
    /**
     * session_key
     */
    public static final String CACHE_SESSION_KEY = "WX:SESSION_KEY:";
}

编写自己的微信配置继承WXPayConfig

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * @author : LiYu
 * @date : 2020-10-24 17:06
 **/
public class AgentWxConfig extends WXPayConfig{
    private byte[] certData;
    public AgentWxConfig() throws Exception {
        String certPath = WxServiceEnvConstants.CERT_PATH;
        File file = new File(certPath);
        InputStream certStream = new FileInputStream(file);
        this.certData = new byte[(int) file.length()];
        certStream.read(this.certData);
        certStream.close();
    }

    @Override
    public String getAppID() {
        return WxServiceEnvConstants.APPLET_APP_ID;
    }

    @Override
    public String getMchID() {
        return WxServiceEnvConstants.MCH_ID;
    }

    @Override
    public String getKey() {
        return WxServiceEnvConstants.API_SECRET_KEY;
    }

    @Override
    public InputStream getCertStream() {
        ByteArrayInputStream certBis = new ByteArrayInputStream(this.certData);
        return certBis;
    }

    @Override
    public int getHttpConnectTimeoutMs() {
        return 8000;
    }

    @Override
    public int getHttpReadTimeoutMs() {
        return 10000;
    }

    @Override
    IWXPayDomain getWXPayDomain() {
        return new IWXPayDomain() {
            @Override
            public void report(String domain, long elapsedTimeMillis, Exception ex) {
            }

            @Override
            public DomainInfo getDomain(WXPayConfig config) {
                return new DomainInfo("api.mch.weixin.qq.com", false);
            }
        };
    }
}

微信支付实现接口

import lombok.Getter;

/**
 * <p>微信支付类型</p>
 *
 * @author : LiYu
 * @date : 2020-10-30 11:43
 **/
@Getter
public enum WechatPaymentType {
    /**支付类型**/
    BUYGOODS(1,"购买商品"),
    RECHARGE(2,"充值货款");

    private final Integer value;
    private final String desc;

    WechatPaymentType(Integer value, String desc){
        this.value = value;
        this.desc = desc;
    }
}
package com.merrisa.modules.agent.user.service;

import java.math.BigDecimal;
import java.util.Map;

/**
 * <p>微信支付</p>
 *
 * @author : LiYu
 * @date : 2020-10-24 17:20
 **/
public interface IWxService {

    /**
     * 微信小程序支付
     * @param orderNumber 订单号
     * @param appletOpenid openId
     * @param money 钱
     * @param ip ip地址
     * @param body 标题
     * @param wechatPaymentType 支付类型
     * @return Result<?>
     */
    Result<?> wxApplet(String orderNumber, String appletOpenid, BigDecimal money, String ip, String body, WechatPaymentType wechatPaymentType);

    /**
     * 微信提现
     * @param openId
     * @param appId
     * @param partnerTradeNo
     * @param money
     * @param ip
     * @param name
     * @param desc 描述
     * @return Map<String, String>
     */
    Map<String, String> wxTransfers(String openId, String appId, String partnerTradeNo,
                                    BigDecimal money, String ip, String name, String desc) throws Exception;
}
package com.merrisa.modules.agent.user.service.impl;

import com.merrisa.modules.agent.user.service.IWxService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.constant.WxServiceEnvConstants;
import org.jeecg.enums.WechatPaymentType;
import org.jeecg.wx.AgentWxConfig;
import org.jeecg.wx.WXPay;
import org.jeecg.wx.WXPayConstants;
import org.jeecg.wx.WXPayUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.HashMap;
import java.util.Map;

/**
 * <h3>jeecg-boot-parent</h3>
 * <p>微信支付</p>
 *
 * @author : LiYu
 * @date : 2020-10-24 17:20
 **/
@Slf4j
@Setter
@Service
public class WxServiceImpl implements IWxService {

    @Value("${agent.wx.pay.notifyUrl}")
    private String notifyUrl;//微信支付回调地址

    @Value("${agent.wx.pay.notifyUrlRecharge}")
    private String notifyUrlRecharge;//微信支付回调地址 根据业务不同配置不同地址


    @Value("${spring.profiles.active}")
    private String profilesActive;


    @Override
    public Result<?> wxApplet(String orderNumber, String appletOpenid, BigDecimal money, String ip, String body, WechatPaymentType wechatPaymentType) {
        AgentWxConfig agentWxConfig = null;
        WXPay wxPay = null;
        Map<String,String> resultMap=new HashMap<>();
        try {
            agentWxConfig = new AgentWxConfig();
            wxPay = new WXPay(agentWxConfig);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //生成的随机字符串
        String generateNonceStr = WXPayUtil.generateNonceStr();
        //统一下单接口参数
        HashMap<String, String> data = new HashMap<>();
        assert agentWxConfig != null;
        data.put("appid", agentWxConfig.getAppID());
        data.put("mch_id", agentWxConfig.getMchID());
        data.put("nonce_str", generateNonceStr);
        data.put("body", body);
        data.put("out_trade_no",orderNumber);
        BigDecimal payMoney = money.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP);
        data.put("total_fee", payMoney.toString());
        data.put("spbill_create_ip", ip);
        data.put("notify_url",wechatPaymentType.equals(WechatPaymentType.BUYGOODS) ? notifyUrl : notifyUrlRecharge);
        data.put("trade_type","JSAPI");
        data.put("openid", appletOpenid);
        data.put("sign_type", WXPayConstants.MD5);
        try {
            assert wxPay != null;
            Map<String, String> rMap = wxPay.unifiedOrder(data);
            log.info("统一下单接口返回: " + rMap);
            String returnCode = rMap.get("return_code");
            String resultCode = rMap.get("result_code");
            String nonceStr = WXPayUtil.generateNonceStr();
            resultMap.put("nonceStr", nonceStr);
            Long timeStamp = System.currentTimeMillis() / 1000;
            if ("SUCCESS".equals(returnCode) && returnCode.equals(resultCode)) {
                String prepayid = rMap.get("prepay_id");
                resultMap.put("package", "prepay_id="+prepayid);
                resultMap.put("signType", "MD5");
                //这边要将返回的时间戳转化成字符串,不然小程序端调用wx.requestPayment方法会报签名错误
                resultMap.put("timeStamp", timeStamp + "");
                //再次签名,这个签名用于小程序端调用wx.requesetPayment方法
                resultMap.put("appId", agentWxConfig.getAppID());
                String sign = WXPayUtil.generateSignature(resultMap, agentWxConfig.getKey());
                resultMap.put("paySign", sign);
                log.info("生成的签名paySign : "+ sign);
                return Result.OK(resultMap);
            }else{
                return Result.error("支付失败");
            }
        }catch (Exception e) {
            e.printStackTrace();
            return Result.error("支付失败");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Map<String, String> wxTransfers(String openId, String appId, String partnerTradeNo, BigDecimal money, String ip, String name, String desc) throws Exception {
        AgentWxConfig agentWxConfig = null;
        WXPay wxPay = null;
        try {
            agentWxConfig = new AgentWxConfig();
            wxPay = new WXPay(agentWxConfig);
        } catch (Exception e) {
            e.printStackTrace();
        }
        Map<String, String> data = new HashMap<>();
        data.put("mch_appid", appId);
        data.put("mchid", WxServiceEnvConstants.MCH_ID);
        data.put("partner_trade_no", partnerTradeNo);
        data.put("openid", openId);
        data.put("check_name", "FORCE_CHECK");
        data.put("re_user_name", name);
        data.put("nonce_str", WXPayUtil.generateNonceStr());
        if(StringUtils.equals(profilesActive,"prod")) {
            data.put("amount", "100");
        }else {
            BigDecimal payMoney = money.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP);
            data.put("amount", payMoney.toString());
        }
        data.put("desc", desc);
        data.put("spbill_create_ip", ip);
        data.put("sign", WXPayUtil.generateSignature(data, WxServiceEnvConstants.API_SECRET_KEY, WXPayConstants.SignType.MD5));
        assert agentWxConfig != null;
        assert wxPay != null;
        String respXml = wxPay.requestWithCert("/mmpaymkttransfers/promotion/transfers",
                data, agentWxConfig.getHttpConnectTimeoutMs(), agentWxConfig.getHttpReadTimeoutMs());
        return WXPayUtil.xmlToMap(respXml);
    }
}

微信支付调用

会返回给前端一个json字符串,前端只需执行以下代码

payNow: function() {
    var that = this;
    wx.request({
      url: app.data.requestUrl + 'weChatPay/doUnifiedOrder',
      header: {
        "Content-Type": "application/json;charset=UTF-8"
      },
      data: {
        requestData: {
          openId: app.globalData.openId
        }
      },
      method: 'POST',
      dataType: 'json',
      responseType: 'text',
      success: function(res) {
        console.log("服务端返回订单号");
        var c=res.data;
        wx.requestPayment({
          timeStamp: res.data.data.timeStamp,
          nonceStr: res.data.data.nonceStr,
          package: res.data.data.package,
          signType: 'MD5',
          paySign: res.data.data.paySign,
          success(res) {
            console.log("统一下单接口成功");
          },
          fail(res) {
            console.log("统一下单接口失败");
          }
        });
      },
      fail: function(res) {},
      complete: function(res) {},
    });
  }

回调方法

@ApiOperation(value = "微信支付回调函数", notes = "wechatPaySuccess")
   @PostMapping("/wechatPaySuccess")
   private String wechatPaySuccess(HttpServletRequest request) throws Exception {
       //从请求中获取到流
       ServletInputStream inStream = request.getInputStream();
       ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
       byte[] buffer = new byte[1024];
       int len = 0;
       while ((len = inStream.read(buffer)) != -1) {
           outSteam.write(buffer, 0, len);
       }
       outSteam.close();
       inStream.close();
       String result = new String(outSteam.toByteArray(), StandardCharsets.UTF_8);
       Map<String, String> map = WXPayUtil.xmlToMap(result);
       // 判断签名是否正确
       boolean signVerified = WXPayUtil.isSignatureValid(result, WxServiceEnvConstants.API_SECRET_KEY);
       if (signVerified) {
           if ("SUCCESS".equals(map.get("result_code"))) {
               String orderNumber = map.get("out_trade_no");
               String tradeType = map.get("trade_type");
               log.info("wxPayNotify : orderNumber={} : tradeType={}", orderNumber, tradeType);
               switch (tradeType) {
                   case "JSAPI":
                      //实现业务
                   default:
                       return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[支付失败]]></return_msg></xml>";
               }
           }
       }else{
           return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[支付失败]]></return_msg></xml>";
       }
       return "<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[支付失败]]></return_msg></xml>";
   }

微信提现

 Map<String, String> resultMap = iWxService.wxTransfers("用户的OpenId", WxServiceEnvConstants.APPLET_APP_ID,"单号","金额" ,ip,"用户真实姓名","标题");
 String returnCode = resultMap.get("return_code");
 if ("SUCCESS".equals(returnCode)) {
 String resultCode = resultMap.get("result_code");
     if ("SUCCESS".equals(resultCode)) {
         //实现业务
     } else {
         String errCode = resultMap.get("err_code");
         String errCodeDes = resultMap.get("err_code_des");
         throw new JeecgBootException(errCode + "---" + errCodeDes);
     }
 } else {
     throw new JeecgBootException(resultMap.get("return_msg"));
 }
}

踩坑

微信官方demo WXPay是这么写的

记住只留下

this.signType = SignType.MD5;

否则会调用失败