SpringBoot集成微信服务商支付(多子商户集成)
- 前言
- 一、前置工作
- 1、获取商户平台的xxx核心参数
- 2、关联对应的小程序(appid)
- 二、SpringBoot集成微信小程序
- 1、引入pom依赖
- 2、yml配置
- 3、java代码文件
- 3.1、Properties 配置类
- 3.2 Configuration 服务类
- 4、使用示例
- 4.1、发起支付(生成前端签名数据)
- 4.2、支付回调解析(支付成功解密数据)
- 4.2.1 接收实体类
- 4.2.2处理代码
- 4.3、订单退款
- 5、更多接口使用请参考
前言
在网上搜了很多,都没有说如何对接微信支付-服务商。于是参考官方代码示例琢磨了一个版本微信官方api文档网站:https://pay.weixin.qq.com/docs/partner/apis/partner-mini-program-payment/partner-mini-prepay.html官方示例demo:https://github.com/wechatpay-apiv3/wechatpay-java
一、前置工作
1、获取商户平台的xxx核心参数
商户号(merchantId)、秘钥(apiV3Key)、商户证书(apiclient_key.pem)、证书序列号(merchantSerialNumber)
2、关联对应的小程序(appid)
二、SpringBoot集成微信小程序
1、引入pom依赖
<!-- 微信支付 --><dependency><groupId>com.github.wechatpay-apiv3</groupId><artifactId>wechatpay-java</artifactId><version>0.2.12</version></dependency>
2、yml配置
pay:# 所关联小程序idappId: wxaxxxxxxxxxxxxxxxmerchantId: 1xxxxxxxx5merchantSerialNumber: xxxxxxxxxxapiV3Key: xxxxxxxxx#支付成功回调地址notifyUrl: https://xxxxxx # 商户平台私钥证书路径privateKeyPath: /xxxxxx/apiclient_key.pem
商户号(merchantId)、秘钥(apiV3Key)、商户证书(apiclient_key.pem)、证书序列号(merchantSerialNumber)
3、java代码文件
3.1、Properties 配置类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;@Data
@ConfigurationProperties(prefix = "pay")
public class WxPayServiceProperties {private String appId;private String merchantId;private String merchantSerialNumber;private String apiV3Key;private String notifyUrl;private String privateKeyPath;}
3.2 Configuration 服务类
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.util.PemUtil;
import com.wechat.pay.java.service.partnerpayments.jsapi.JsapiService;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.RefundService.Builder;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxRuntimeException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;/*** @author tm* date 2023/5/15* @version 1.0*/
@Slf4j
@Configuration
@EnableConfigurationProperties(WxPayServiceProperties.class)
public class WxPayServiceConfiguration {private final WxPayServiceProperties properties;public static JsapiService service;private static String privateKeyPath;public static RefundService refundService;@Autowiredpublic WxPayServiceConfiguration(WxPayServiceProperties properties) {this.properties = properties;}@PostConstructpublic void init() {WxPayServiceProperties properties = this.properties;if (properties == null) {throw new WxRuntimeException("添加下相关配置,注意别配错了!");}String path = this.getClass().getResource("/").getPath();privateKeyPath = path + "cert/apiclient_key.pem";if (StringUtils.hasText(properties.getPrivateKeyPath())) {privateKeyPath = properties.getPrivateKeyPath();}log.info("证书地址:{}", privateKeyPath);try {// 初始化商户配置Config config =new RSAAutoCertificateConfig.Builder().merchantId(properties.getMerchantId())// 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名.privateKeyFromPath(privateKeyPath).merchantSerialNumber(properties.getMerchantSerialNumber()).apiV3Key(properties.getApiV3Key()).build();service = new JsapiService.Builder().config(config).build();log.info("【微信】支付服务加载完成");refundService = new Builder().config(config).build();log.info("【微信】退款服务加载完成");} catch (Exception e) {e.printStackTrace();}}public static String createSign(JSONObject packageParams) {String sign = "";try {StringBuffer append = new StringBuffer();append.append(packageParams.getString("appId")).append("\n");append.append(packageParams.getString("timeStamp")).append("\n");append.append(packageParams.getString("nonceStr")).append("\n");append.append(packageParams.getString("package")).append("\n");Signature Sign = Signature.getInstance("SHA256withRSA");PrivateKey privateKey = PemUtil.loadPrivateKeyFromPath(privateKeyPath);Sign.initSign(privateKey);Sign.update(append.toString().getBytes(StandardCharsets.UTF_8));byte[] signed = Sign.sign();sign = Base64.getEncoder().encodeToString(signed);} catch (Exception ex) {log.error("生成签名失败");}return sign;}}
4、使用示例
4.1、发起支付(生成前端签名数据)
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Amount;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.Payer;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.partnerpayments.jsapi.model.PrepayResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.Date;/*** Author tm* Date 2024/7/4* Version 1.0*/
@Slf4j
@Validated
@RestController
@RequestMapping("/wx/pay")
public class WxPayController2 {@Resourceprivate WxPayServiceProperties properties;@RequestMapping("createOrder")@Transactionalpublic Result plantPayOrder(PlantRequest paramRequest) {PrepayRequest payRequest = new PrepayRequest();// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义// 调用接口payRequest.setSpAppid(properties.getAppId());payRequest.setSpMchid(properties.getMerchantId());payRequest.setNotifyUrl(properties.getNotifyUrl());payRequest.setSubMchid();// todo 子商户号payRequest.setDescription("备注");payRequest.setOutTradeNo(DateUtils.formatDateMS());//System.currentTimeMillis() + ""Amount amount = new Amount();amount.setTotal();// todo 支付金额payRequest.setAmount(amount);Payer payer = new Payer();payer.setSpOpenid();// todo 小程序用户appidpayRequest.setPayer(payer);payRequest.setAttach("");// todo 附加数据 可空PrepayResponse prepayResponse = WxPayServiceConfiguration.service.prepay(payRequest);if (!StringUtils.hasText(prepayResponse.getPrepayId())) {return Result.error("下单失败");}JSONObject resultJson = new JSONObject();resultJson.put("appId", properties.getAppId());resultJson.put("timeStamp", (new Date()).getTime() / 1000);resultJson.put("nonceStr", DateUtils.currentTimestamp() + DateUtils.buildRandom(4));resultJson.put("package", "prepay_id=" + prepayResponse.getPrepayId());resultJson.put("signType", "RSA");resultJson.put("paySign", WxPayServiceConfiguration.createSign(resultJson));return Result.ok("下单成功", resultJson);}}
4.2、支付回调解析(支付成功解密数据)
4.2.1 接收实体类
import lombok.Data;@Data
public class WxPayCallBackRequest {private String id;private String create_time;private String resource_type;private String event_type;private String summary;private WxPayCallBackResourceVo resource;}
import lombok.Data;/*** Author tm* Date 2024/7/4* Version 1.0*/
@Data
public class WxPayCallBackResourceVo {private String original_type;private String algorithm;private String ciphertext;private String associated_data;private String nonce;}
4.2.2处理代码
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wechat.pay.java.service.partnerpayments.nativepay.model.Transaction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;/*** Author tm* Date 2024/7/4* Version 1.0*/
@Slf4j
@Validated
@RestController
@RequestMapping("/wx/pay")
public class WxPayController2 {@Resourceprivate WxPayServiceProperties properties;@RequestMapping("wxPayCallBack")@Transactionalpublic void wxPayCallBack(@RequestBody WxPayCallBackRequest request, HttpServletResponse response) {log.info("【微信支付回调参数】参数:{}", JSON.toJSONString(request));try {response.getWriter().write("SUCCESS");} catch (IOException e) {e.printStackTrace();}WxPayCallBackResourceVo resource = request.getResource();try {Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");SecretKeySpec key = new SecretKeySpec(properties.getApiV3Key().getBytes(StandardCharsets.UTF_8), "AES");GCMParameterSpec spec = new GCMParameterSpec(128, resource.getNonce().getBytes(StandardCharsets.UTF_8));cipher.init(Cipher.DECRYPT_MODE, key, spec);cipher.updateAAD(resource.getAssociated_data().getBytes(StandardCharsets.UTF_8));String parseString = new String(cipher.doFinal(Base64.getDecoder().decode(resource.getCiphertext())), "UTF-8");Transaction transaction = JSONObject.parseObject(parseString, Transaction.class);log.info("【微信支付回调参数】解析后参数:{}", transaction);} catch (Exception e) {e.printStackTrace();}}}
4.3、订单退款
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.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@Slf4j
@Validated
@RestController
@RequestMapping("/wx/pay")
public class WxPayController2 {@Resourceprivate WxPayServiceProperties properties;@RequestMapping("refund")@Transactionalpublic Result cancelClaimPlan(String claimId) {CreateRequest request = new CreateRequest();request.setSubMchid(); // todo 子商户request.setTransactionId(); // todo 微信支付回调唯一标识request.setOutRefundNo(DateUtils.formatDateMS()); // todo 商户退款单号request.setReason("取消认领,进行退款");AmountReq amountReq = new AmountReq();amountReq.setTotal(); // todo 原订单金额amountReq.setRefund(); // todo 退款金额amountReq.setCurrency("CNY");request.setAmount(amountReq);log.info("【取消】【退款】{}", request);Refund refund = WxPayServiceConfiguration.refundService.create(request);log.info("【取消】【退款结果】{}", refund);return Result.ok("退款成功", null);}}
5、更多接口使用请参考
com.wechat.pay.java.service.partnerpayments.jsapi.JsapiService