– 微信支付 –
前提条件
- 微信支付商户号:必须有一个有效的微信支付商户号。
- 公众号或小程序:需要有一个经过认证的微信公众号或小程序,用于生成支付授权。
- 微信支付API证书:用于在服务器端调用微信支付API时进行身份验证
引入依赖
<!--微信支付-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.12</version>
</dependency>
编写配置文件
- 这里默认已经获得微信支付需要的相关参数
- 下面直接进行代码Coding阶段
wx:
# 微信小程序appid
app-id: wxcb25xxxxxxxxxxxx
# 商户号
mch-id: 16xxxxxxxx
# 证书序列号
mch-serial-no: 59BAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# api密钥
api-key: tonxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 回调接口地址(订单不做回调可以不用配)
notify-url: https://15x.xx.xxx.xx/pay/payCallBack
# 证书地址
key-path: /apiclient_key.pem
cert-path: /apiclient_cert.pem
cert-p12-path: /apiclient_cert.p12
msgDataFormat: JSON
编写配置WxProperties
@Component
@ConfigurationProperties(prefix = "wx")
@Data
@ToString
public class WxProperties {
private String appId;
private String mchId;
private String keyPath;
private String certPath;
private String apiKey;
private String notifyUrl;
private String mchSerialNo;
private String msgDataFormat;
}
编写配置WechatAutoConfiguration
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import com.wechat.pay.java.core.Config;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.cert.*;
@Slf4j
@Configuration
@RequiredArgsConstructor
public class WechatAutoConfiguration {
private final WxProperties wxProperties;
private final ResourceLoader resourceLoader;
private static final String CLASS_PATH = "classpath:";
@Bean
public Config config() throws IOException {
String merchantSerialNumber = getCertificateSerialNumber(wxProperties.getCertPath());
String privateKey = readResourceAsString(wxProperties.getKeyPath());
return new RSAAutoCertificateConfig.Builder()
.merchantId(wxProperties.getMchId())
.privateKey(privateKey)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(wxProperties.getApiKey())
.build();
}
@Bean
public JsapiServiceExtension jsapiServiceExtension(Config config) {
return new JsapiServiceExtension.Builder().config(config).build();
}
@Bean
public NotificationParser notificationParser(Config config) {
return new NotificationParser((NotificationConfig) config);
}
private String readResourceAsString(String path) throws IOException {
Resource resource = resourceLoader.getResource(CLASS_PATH + path);
try (InputStream inputStream = resource.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
return stringBuilder.toString();
}
}
private String getCertificateSerialNumber(String certPath) throws IOException {
Resource resource = resourceLoader.getResource(CLASS_PATH + certPath);
try (InputStream inputStream = resource.getInputStream()) {
X509Certificate certificate = getCertificate(inputStream);
return certificate.getSerialNumber().toString(16).toUpperCase();
}
}
public static X509Certificate getCertificate(InputStream inputStream) {
try {
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
cert.checkValidity();
return cert;
} catch (CertificateExpiredException e) {
throw new RuntimeException("证书已过期", e);
} catch (CertificateNotYetValidException e) {
throw new RuntimeException("证书尚未生效", e);
} catch (CertificateException e) {
throw new RuntimeException("无效的证书", e);
}
}
}
JSAPI下单 - JSAPI支付 | 微信支付商户文档中心
微信支付相关代码
Controller控制器代码
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/wechat")
public class WeChatController {
@Autowired
private WeChatService wechatService;
@ApiOperation("微信预支付")
@PostMapping("/prePay")
public R<?> createPreOrder(@RequestBody PrePayReq prePayReq) {
return R.ok(wechatService.prepayWithRequest(prePayReq));
}
@ApiOperation("查询订单")
@GetMapping("/queryOrder")
public R<?> queryOrder(@RequestParam String outTradeNo) {
return R.ok(wechatService.queryStatus(outTradeNo));
}
@ApiOperation("取消订单")
@PostMapping("/closeOrder")
public R<?> closeOrder(@RequestParam String outTradeNo) {
return R.ok(wechatService.closeOrder(outTradeNo));
}
}
Service接口层代码
import com.itheima.mp.domain.R;
import com.itheima.mp.domain.wechat.PrePayReq;
public interface WeChatService {
R<?> prepayWithRequest(PrePayReq prePayReq);
R<?> queryStatus(String outTradeNo);
R<?> closeOrder(String outTradeNo);
}
ServiceImpl实现层代码
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.security.SecureRandom;
@Slf4j
@Service
@RequiredArgsConstructor
public class WeChatServiceImpl implements WeChatService {
private final WxProperties wxProperties;
private final JsapiServiceExtension jsapiService;
@Override
public R<?> prepayWithRequest(PrePayReq prePayReq) {
if (prePayReq == null || prePayReq.getAmount() == null || prePayReq.getDescription() == null || prePayReq.getOpenid() == null) {
return R.fail(HttpStatus.BAD_REQUEST, "参数错误");
}
PrepayRequest request = new PrepayRequest();
String outTradeNo = System.currentTimeMillis() + String.format("%06d", new SecureRandom().nextInt(999999));
request.setAppid(wxProperties.getAppId());
request.setMchid(wxProperties.getMchId());
request.setDescription(prePayReq.getDescription());
request.setOutTradeNo(outTradeNo);
request.setNotifyUrl(wxProperties.getNotifyUrl());
Amount amount = new Amount();
amount.setTotal(prePayReq.getAmount());
amount.setCurrency("CNY");
request.setAmount(amount);
Payer payer = new Payer();
payer.setOpenid(prePayReq.getOpenid());
request.setPayer(payer);
try {
PrepayWithRequestPaymentResponse response = jsapiService.prepayWithRequestPayment(request);
return R.ok(response);
} catch (ServiceException e) {
return R.fail(e.getHttpStatusCode(), e.getErrorMessage());
}
}
@Override
public R<?> queryStatus(String outTradeNo) {
if (outTradeNo == null || outTradeNo.isEmpty()) {
return R.fail(HttpStatus.BAD_REQUEST, "参数错误");
}
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setOutTradeNo(outTradeNo);
request.setMchid(wxProperties.getMchId());
try {
Transaction response = jsapiService.queryOrderByOutTradeNo(request);
return R.ok(response);
} catch (ServiceException e) {
return R.fail(e.getHttpStatusCode(), e.getErrorMessage());
}
}
@Override
public R<?> closeOrder(String outTradeNo) {
if (outTradeNo == null || outTradeNo.isEmpty()) {
return R.fail(HttpStatus.BAD_REQUEST, "参数错误");
}
CloseOrderRequest request = new CloseOrderRequest();
request.setOutTradeNo(outTradeNo);
request.setMchid(wxProperties.getMchId());
try {
jsapiService.closeOrder(request);
return R.ok();
} catch (ServiceException e) {
return R.fail(e.getHttpStatusCode(), e.getErrorMessage());
}
}
}
部分实体类
统一响应实体类
package com.itheima.mp.domain;
import java.io.Serializable;
public class R<T> implements Serializable
{
private static final long serialVersionUID = 1L;
public static final int SUCCESS = 200;
public static final int FAIL = 500;
private int code;
private String msg;
private T data;
public static <T> R<T> ok()
{
return restResult(null, SUCCESS, "操作成功");
}
public static <T> R<T> ok(String msg)
{
return restResult(null, SUCCESS, msg);
}
public static <T> R<T> ok(T data)
{
return restResult(data, SUCCESS, "操作成功");
}
public static <T> R<T> ok(T data, String msg)
{
return restResult(data, SUCCESS, msg);
}
public static <T> R<T> fail()
{
return restResult(null, FAIL, "操作失败");
}
public static <T> R<T> fail(String msg)
{
return restResult(null, FAIL, msg);
}
public static <T> R<T> fail(T data)
{
return restResult(data, FAIL, "操作失败");
}
public static <T> R<T> fail(T data, String msg)
{
return restResult(data, FAIL, msg);
}
public static <T> R<T> fail(int code, String msg)
{
return restResult(null, code, msg);
}
private static <T> R<T> restResult(T data, int code, String msg)
{
R<T> apiResult = new R<>();
apiResult.setCode(code);
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}
public int getCode()
{
return code;
}
public void setCode(int code)
{
this.code = code;
}
public String getMsg()
{
return msg;
}
public void setMsg(String msg)
{
this.msg = msg;
}
public T getData()
{
return data;
}
public void setData(T data)
{
this.data = data;
}
}
预支付订单请求实体类
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel(description = "订单预支付实体类")
public class PrePayReq {
@ApiModelProperty("商品描述")
private String description;
@ApiModelProperty("订单金额")
private Integer amount;
@ApiModelProperty("用户标识")
private String openid;
}
响应状态码枚举类
public class HttpStatus
{
public static final int SUCCESS = 200;
public static final int CREATED = 201;
public static final int ACCEPTED = 202;
public static final int NO_CONTENT = 204;
public static final int MOVED_PERM = 301;
public static final int SEE_OTHER = 303;
public static final int NOT_MODIFIED = 304;
public static final int BAD_REQUEST = 400;
public static final int UNAUTHORIZED = 401;
public static final int FORBIDDEN = 403;
public static final int NOT_FOUND = 404;
public static final int BAD_METHOD = 405;
public static final int CONFLICT = 409;
public static final int UNSUPPORTED_TYPE = 415;
public static final int ERROR = 500;
public static final int NOT_IMPLEMENTED = 501;
public static final int WARN = 601;
}