java 对接微信小程序支付

202 阅读4分钟

一:配置信息

在yml文件中填好以下微信支付的配置,记得enabled一定要true,因为后面用到的注解是必须要这个值为true,springboot才能识别到那个bean

wechat:  
enabled: false  
appId: 你的微信服务号信息(h5的时候需要,小程序的时候不需要)  
secret: 你的微信服务号信息(h5的时候需要,小程序的时候不需要)  
merchantId: 微信支付商户号  
privateKeyPath: 微信支付密钥地址相对地址  
merchantSerialNumber: 微信支付密钥对应的序列号  
apiV3key: 微信支付apiV3key  
notifyUrl: 微信支付回调地址  
miniProgramAppId: 小程序apppid(h5的时候不需要)  
miniProgramSecret: 小程序Secret(h5的时候不需要)

引入工具包

<dependency>  
<groupId>com.github.wechatpay-apiv3</groupId>  
<artifactId>wechatpay-java</artifactId>  
<version>0.2.9</version>  
</dependency>  
  
<dependency>  
<groupId>cn.hutool</groupId>  
<artifactId>hutool-all</artifactId>  
<version>5.7.13</version>  
</dependency>

service中的支付方法

返回给前端调用微信支付所有必要的参数,传入的是您自己的订单信息

/**  
* 订单支付  
*  
* @param req 支付请求  
* @return  
*/  

@Autowired(required = false)  
private WechatPayService wechatPayService;


public OrderPayVO orderPay(PointOrder req) {    
String orderDesc = "测试订单描述文字";  
String openId = "oBV-e5OwrFph1yfp0nHREKJaXeNY";  
String appId = "填写您的appId";  
//请开启微信支付 wechat.enabled=true  
String prepayId;  
try {  
     //调用wx的jsapi拿prepayId,返回签名等信息  
     prepayId = wechatPayService.jsapiPay(  
     String.valueOf(req.getOrderNumber()),  
     orderDesc,  
     (int) 1,  
     openId,  
     100L,  
     appId  
);  
} catch (Exception e) {  
    throw new RuntimeException(e);  
}  
OrderPayVO response = new OrderPayVO();  
    response.setPayType(2);  
    String nonceStr = WechatPayUtil.generateNonceStr();  
    long timeStamp = WechatPayUtil.getCurrentTimestamp();  
    prepayId = "prepay_id=" + prepayId;  
    String signType = "RSA";  
    String paySign = null;  
    String signatureStr = Stream.of(appId, String.valueOf(timeStamp), nonceStr, prepayId)  
    .collect(Collectors.joining("\n", "", "\n"));  
    try {  
        paySign = WechatPayUtil.getSign(signatureStr, WechatPayData.privateKeyPath);  
    } catch (Exception e) {  
    throw new RuntimeException("支付失败");  
}  
    response.setAppId(appId);  
    response.setTimeStamp(String.valueOf(timeStamp));  
    response.setNonceStr(nonceStr);  
    response.setSignType(signType);  
    response.setPackage_(prepayId);  
    response.setPaySign(paySign);  
    return response;  
}

WechatPayService类

传入金额等参数调用JsapiService中的prepay方法

package com.ruoyi.wechat;  
  
import com.wechat.pay.java.service.payments.jsapi.JsapiService;  
import com.wechat.pay.java.service.payments.jsapi.model.Amount;  
import com.wechat.pay.java.service.payments.jsapi.model.Payer;  
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;  
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;  
import com.wechat.pay.java.service.refund.RefundService;  
import com.wechat.pay.java.service.refund.model.AmountReq;  
import com.wechat.pay.java.service.refund.model.CreateRequest;  
import com.wechat.pay.java.service.refund.model.Refund;  
import lombok.extern.slf4j.Slf4j;  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;  
import org.springframework.stereotype.Service;  
  
  
@Service  
@Slf4j  
@ConditionalOnProperty(prefix = "wechat", name = "enabled", havingValue = "true")  
public class WechatPayService {  
  
@Autowired  
private JsapiService service;  
  
@Autowired  
private RefundService refundService;  
  
/**  
* jsapi下单  
* @param orderNo 订单号  
* @param desc 订单描述  
* @param totalAmount 总金额,单位:分  
* @param openId 用户openid  
* @return prepay_id  
*/  
public String jsapiPay(String orderNo,String desc,Integer totalAmount,String openId, Long memberId,String appId){  
    PrepayRequest prepayRequest = new PrepayRequest();  
    prepayRequest.setAppid(appId);  
    prepayRequest.setMchid(WechatPayData.merchantId);  
    prepayRequest.setDescription(desc);  
    prepayRequest.setOutTradeNo(orderNo);  
    prepayRequest.setAttach(String.valueOf(memberId));  
    prepayRequest.setNotifyUrl(WechatPayData.notifyUrl);  
    Amount amount = new Amount();  
    amount.setTotal(totalAmount);  
    prepayRequest.setAmount(amount);  
    Payer payer = new Payer();  
    payer.setOpenid(openId);  
    prepayRequest.setPayer(payer);  
    PrepayResponse response = service.prepay(prepayRequest);  
    return response.getPrepayId();  
}  
  
public Refund refundPay(String refundId,String payId,String notifyUrl,Long refundAmount, Long totalAmount,String reason) {  
    //请求参数  
    CreateRequest request = new CreateRequest();  
    request.setReason(reason);  
    //设置退款金额 根据自己的实际业务自行填写  
    AmountReq amountReq = new AmountReq();  
    amountReq.setRefund(refundAmount);  
    amountReq.setTotal(totalAmount);  
    amountReq.setCurrency("CNY");  
    request.setAmount(amountReq);  
    //支付成功后回调回来的transactionId 按照实际情况填写  
    request.setOutTradeNo(payId);  
    //支付成功后回调回来的transactionId 按照实际情况填写  
    request.setOutRefundNo(refundId);  
    //退款成功的回调地址  
    request.setNotifyUrl(notifyUrl);  
    //发起请求,申请退款  
    return refundService.create(request);  
    }  
}

WechatConfig类

package com.ruoyi.wechat;  
  
import com.wechat.pay.java.service.payments.jsapi.JsapiService;  
import com.wechat.pay.java.service.refund.RefundService;  
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;  
import org.springframework.context.annotation.Bean;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.context.annotation.DependsOn;  
  
@Configuration  
@DependsOn("WechatPayData")  
@ConditionalOnProperty(prefix = "wechat", name = "enabled", havingValue = "true")  
public class WechatConfig {  
  
@Bean  
public JsapiService jsapiService(){  
 return new JsapiService.Builder().config(WechatPayConfig.getInstance()).build();  
}  
  
@Bean  
public RefundService refundService(){  
  return new RefundService.Builder().config(WechatPayConfig.getInstance()).build();  
 }  
}

WechatPayConfig

package com.ruoyi.wechat;  
  
import com.wechat.pay.java.core.Config;  
import com.wechat.pay.java.core.RSAAutoCertificateConfig;  
  
public class WechatPayConfig {  
  
private static Config wechatPayConfig;  
  
private WechatPayConfig(){}  
  
public static Config getInstance() {  
  
  
if (wechatPayConfig == null) {  
    wechatPayConfig = new RSAAutoCertificateConfig.Builder()  
    .merchantId(WechatPayData.merchantId)  
    .privateKeyFromPath(WechatPayData.privateKeyPath)  
    .merchantSerialNumber(WechatPayData.merchantSerialNumber)  
    .apiV3Key(WechatPayData.apiV3key)  
    .build();  
    }  
    return wechatPayConfig;  
    }  
}

WechatPayData

这个类是映射配置文件中的微信支付配置

package com.ruoyi.wechat;  
  
import lombok.Data;  
import org.springframework.boot.context.properties.ConfigurationProperties;  
import org.springframework.stereotype.Component;  
  
@Data  
@ConfigurationProperties(prefix = "wechat")  
@Component("WechatPayData")  
public class WechatPayData {  
  
/** 商户号 */  
public static String appId;  
public static String secret;  
public static String merchantId;  
/** 商户API私钥路径 */  
public static String privateKeyPath;  
/** 商户证书序列号 */  
public static String merchantSerialNumber;  
/** 商户APIV3密钥 */  
public static String apiV3key;  
public static String notifyUrl;  
  
public static String miniProgramAppId;  
public static String miniProgramSecret;  
  
// 设置商户号  
public void setAppId(String appId) {  
    WechatPayData.appId = appId;  
}  
  
// 设置密钥  
public void setSecret(String secret) {  
    WechatPayData.secret = secret;  
}  
  
// 设置商户号  
public void setMerchantId(String merchantId) {  
    WechatPayData.merchantId = merchantId;  
}  
  
// 设置商户API私钥路径  
public void setPrivateKeyPath(String privateKeyPath) {  
    WechatPayData.privateKeyPath = privateKeyPath;  
}  
  
// 设置商户证书序列号  
public void setMerchantSerialNumber(String merchantSerialNumber) {  
    WechatPayData.merchantSerialNumber = merchantSerialNumber;  
}  
  
// 设置商户APIV3密钥  
public void setApiV3key(String apiV3key) {  
    WechatPayData.apiV3key = apiV3key;  
}  
  
// 设置通知地址  
public void setNotifyUrl(String notifyUrl) {  
    WechatPayData.notifyUrl = notifyUrl;  
}  
  
// 设置小程序AppId  
public void setMiniProgramAppId(String miniProgramAppId) {  
    WechatPayData.miniProgramAppId = miniProgramAppId;  
}  
  
// 设置小程序密钥  
public void setMiniProgramSecret(String miniProgramSecret) {  
    WechatPayData.miniProgramSecret = miniProgramSecret;  
    }  
}

WechatPayUtil

package com.ruoyi.wechat;  
  
  
import cn.hutool.crypto.PemUtil;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.util.Base64Utils;  
  
import java.io.IOException;  
import java.io.InputStream;  
import java.net.URISyntaxException;  
import java.nio.charset.StandardCharsets;  
import java.nio.file.Files;  
import java.nio.file.Paths;  
import java.security.*;  
import java.util.Random;  
  
  
public class WechatPayUtil {  
  
private static final String SYMBOLS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";  
  
private static final Random RANDOM = new SecureRandom();  
  
  
public static String getSign(String signatureStr,String privateKey) throws InvalidKeyException, NoSuchAlgorithmException, SignatureException, IOException, URISyntaxException {  
    //replace 根据实际情况,不一定都需要  
    String replace = privateKey.replace("\\n", "\n");  
    InputStream certStream = Files.newInputStream(Paths.get(privateKey));  
    PrivateKey merchantPrivateKey = PemUtil.readPemPrivateKey(certStream);  
    Signature sign = Signature.getInstance("SHA256withRSA");  
    sign.initSign(merchantPrivateKey);  
    sign.update(signatureStr.getBytes(StandardCharsets.UTF_8));  
    return Base64Utils.encodeToString(sign.sign());  
}  
  
/**  
* 获取随机字符串 Nonce Str  
*  
* @return String 随机字符串  
*/  
public static String generateNonceStr() {  
char[] nonceChars = new char[32];  
for (int index = 0; index < nonceChars.length; ++index) {  
    nonceChars[index] = SYMBOLS.charAt(RANDOM.nextInt(SYMBOLS.length()));  
    }  
    return new String(nonceChars);  
}  
  
  
/**  
* 日志  
* @return  
*/  
public static Logger getLogger() {  
    Logger logger = LoggerFactory.getLogger("wxpay java sdk");  
    return logger;  
}  
  
/**  
* 获取当前时间戳,单位秒  
* @return  
*/  
public static long getCurrentTimestamp() {  
    return System.currentTimeMillis()/1000;  
}  
  
/**  
* 获取当前时间戳,单位毫秒  
* @return  
*/  
public static long getCurrentTimestampMs() {  
        return System.currentTimeMillis();  
}  
  
}

把以上几个类全部放在项目中就ok了 此代码为不含订单逻辑的