准备工作
微信支付开发前,需要先获取商家信息,包括商户号
、AppId
、证书和密钥
:
- • 获取商户号:微信商户平台
申请成为商户
=>提交资料
=>签署协议
=>获取商户号
; - • 获取AppID:微信公众平台
注册服务号
=>服务号认证
=>获取APPID
=>绑定商户号
; - • 申请商户证书:登录商户平台 =>
选择 账户中心
=>安全中心
=>API安全
=>申请API证书 包括商户证书和商户私钥
; - • 获取微信的证书:获取APIv3秘钥 登录商户平台 =>
选择 账户中心
=>安全中心
=>API安全
=>设置APIv3密钥
。
工具类
添加依赖
引入微信支付开放平台的 API 依赖,以便能够使用 Java 调用相关 API 接口。
微信支付 Java SDK 地址:
https://github.com/wechatpay-apiv3/wechatpay-java
代码语言:javascript复制<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.12</version>
</dependency>
支付工具类
- • JSAPI支付下单:生成预支付订单并返回支付参数
- • 关闭订单
- • 微信支付订单号查询订单
- • 商户订单号查询订单
- • 申请退款:微信支付订单号和商家订单号二选一;
- • 退款查询
package com.tansci.utils;
import com.alibaba.fastjson2.JSON;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.HttpException;
import com.wechat.pay.java.core.exception.MalformedMessageException;
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 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.QueryByOutRefundNoRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.system.ApplicationHome;
import java.util.Objects;
/**
* @path:com.tansci.utils.WxPayUtil.java
* @className:WxPayUtil.java
* @description: 微信小程序支付工具类
* @author:tanyp
* @editNote:
*/
@Slf4j
public class WxPayUtil {
// appID
private static String appid = "wx1fdfgfh149c6353";
// 商户号
private static String merchantId = "124589286";
// 商户证书序列号
private static String merchantSerialNumber = "3D3D4ADM154FDG44DFG45GF1SDF4JFA8DF95";
// 商户APIV3密钥
private static String apiV3Key = "yyh1466255d14dggh524dg666983286";
// 商户API私钥
private static String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDPH5wD/SO6 3jMUdUio0awAwR Ni7s22Csqk8EQLoYbeOzCN 4 bKJ4/jAuJ8CuqaCCCXtCDRlriLW35C7uzeuOiL3tN";
// 支付回调地址
private static String payNotifyUrl = "https://tansci.top/api/pay/payCallback";
// 支付回调地址
private static String refunNotifyUrl = "https://tansci.top/api/pay/refunCallback";
private static JsapiServiceExtension jsapiService;
private static RefundService refundService;
public static void initConfig() {
// 初始化商户配置
Config config = new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKey(privateKey)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
// 初始化服务
jsapiService = new JsapiServiceExtension.Builder().config(config).build();
refundService = new RefundService.Builder().config(config).build();
}
/**
* @methodName:jsapi
* @description:JSAPI支付下单
* @author:tanyp
* @Params: [orderId, goodsName, openId, amount]
* @Return: java.lang.String
* @editNote:
*/
public static Object jsapi(String orderId, String goodsName, String openId, Integer amount) {
try {
if (Objects.isNull(jsapiService)) {
initConfig();
}
PrepayRequest request = new PrepayRequest();
request.setAppid(appid);
request.setMchid(merchantId);
request.setOutTradeNo(orderId);
request.setDescription(goodsName);
request.setNotifyUrl(payNotifyUrl);
// 支付者
Payer payer = new Payer();
payer.setOpenid(openId);
request.setPayer(payer);
// 订单金额,单位为分(正数)
Amount _amount = new Amount();
_amount.setCurrency("CNY");
_amount.setTotal(amount);
request.setAmount(_amount);
log.info("JSAPI 支付下单请求参数:{}", JSON.toJSON(request));
PrepayWithRequestPaymentResponse response = jsapiService.prepayWithRequestPayment(request);
log.info("JSAPI 支付下单返回参数:{}", JSON.toJSON(response));
return response;
} catch (HttpException e) {
// 发送HTTP请求失败
log.error("支付异常,发送HTTP请求失败:{}", e);
return e.getMessage();
} catch (ServiceException e) {
log.error("支付异常,服务返回状态异常:{}", e);
return e.getErrorMessage();
} catch (MalformedMessageException e) {
log.error("支付异常,服务返回成功,返回体类型不合法,或者解析返回体失败:{}", e);
return e.getMessage();
}
}
/**
* @methodName:closeOrder
* @description:关闭订单
* @author:tanyp
* @Params: [orderId]
* @Return: java.lang.String
* @editNote:
*/
public static String closeOrder(String orderId) {
try {
if (Objects.isNull(jsapiService)) {
initConfig();
}
CloseOrderRequest request = new CloseOrderRequest();
request.setMchid(merchantId);
request.setOutTradeNo(orderId);
jsapiService.closeOrder(request);
return "ok";
} catch (HttpException e) {
// 发送HTTP请求失败
log.error("支付异常,发送HTTP请求失败:{}", e);
return e.getMessage();
} catch (ServiceException e) {
log.error("支付异常,服务返回状态异常:{}", e);
return e.getErrorMessage();
} catch (MalformedMessageException e) {
log.error("支付异常,服务返回成功,返回体类型不合法,或者解析返回体失败:{}", e);
return e.getMessage();
}
}
/**
* @methodName:queryOrderById
* @description:微信支付订单号查询订单
* @author:tanyp
* @Params: [transactionId]
* @Return: com.wechat.pay.java.service.payments.model.Transaction
* @editNote:
*/
public static Transaction queryOrderById(String transactionId) {
try {
if (Objects.isNull(jsapiService)) {
initConfig();
}
QueryOrderByIdRequest request = new QueryOrderByIdRequest();
request.setMchid(merchantId);
request.setTransactionId(transactionId);
return jsapiService.queryOrderById(request);
} catch (HttpException e) {
// 发送HTTP请求失败
log.error("支付异常,发送HTTP请求失败:{}", e);
} catch (ServiceException e) {
log.error("支付异常,服务返回状态异常:{}", e);
} catch (MalformedMessageException e) {
log.error("支付异常,服务返回成功,返回体类型不合法,或者解析返回体失败:{}", e);
}
return null;
}
/**
* @methodName:queryOrderByOutTradeNo
* @description:商户订单号查询订单
* @author:tanyp
* @Params: [orderId]
* @Return: com.wechat.pay.java.service.payments.model.Transaction
* @editNote:
*/
public static Transaction queryOrderByOutTradeNo(String orderId) {
try {
if (Objects.isNull(jsapiService)) {
initConfig();
}
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
request.setMchid(merchantId);
request.setOutTradeNo(orderId);
return jsapiService.queryOrderByOutTradeNo(request);
} catch (HttpException e) {
// 发送HTTP请求失败
log.error("支付异常,发送HTTP请求失败:{}", e);
} catch (ServiceException e) {
log.error("支付异常,服务返回状态异常:{}", e);
} catch (MalformedMessageException e) {
log.error("支付异常,服务返回成功,返回体类型不合法,或者解析返回体失败:{}", e);
}
return null;
}
/**
* @methodName:refund
* @description:申请退款
* @author:tanyp
* @Params: [transactionId, orderId, refunId, reason, refunAmount, payAmount]
* @Return: com.wechat.pay.java.service.refund.model.Refund
* @editNote:
*/
public static Refund refund(String transactionId, String orderId, String refunId, String reason, Integer refunAmount, Integer payAmount) {
try {
if (Objects.isNull(refundService)) {
initConfig();
}
CreateRequest request = new CreateRequest();
if (Objects.isNull(transactionId)) {
request.setTransactionId(transactionId);
}
request.setOutTradeNo(orderId);
request.setOutRefundNo(refunId);
request.setNotifyUrl(refunNotifyUrl);
request.setReason(reason);
// 订单金额,单位为分(正数)
AmountReq _amount = new AmountReq();
_amount.setCurrency("CNY");
_amount.setRefund(refunAmount.longValue());
_amount.setTotal(payAmount.longValue());
request.setAmount(_amount);
return refundService.create(request);
} catch (HttpException e) {
// 发送HTTP请求失败
log.error("退款异常,发送HTTP请求失败:{}", e);
} catch (ServiceException e) {
log.error("退款异常,服务返回状态异常:{}", e);
} catch (MalformedMessageException e) {
log.error("退款异常,服务返回成功,返回体类型不合法,或者解析返回体失败:{}", e);
}
return null;
}
/**
* @methodName:queryByOutRefundNo
* @description:退款查询
* @author:tanyp
* @Params: [refunId]
* @Return: com.wechat.pay.java.service.refund.model.Refund
* @editNote:
*/
public static Refund queryByOutRefundNo(String refunId) {
try {
if (Objects.isNull(refundService)) {
initConfig();
}
QueryByOutRefundNoRequest request = new QueryByOutRefundNoRequest();
request.setOutRefundNo(refunId);
return refundService.queryByOutRefundNo(request);
} catch (HttpException e) {
// 发送HTTP请求失败
log.error("退款异常,发送HTTP请求失败:{}", e);
} catch (ServiceException e) {
log.error("退款异常,服务返回状态异常:{}", e);
} catch (MalformedMessageException e) {
log.error("退款异常,服务返回成功,返回体类型不合法,或者解析返回体失败:{}", e);
}
return null;
}
}
回调通知
创建一个公开的 HTTP 端点,接受来自微信支付的支付、退款回调通知。
PayController.java
代码语言:javascript复制package com.tansci.controller;
import com.tansci.common.WrapMapper;
import com.tansci.common.Wrapper;
import com.tansci.service.PayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
/**
* @ClassName: PayController.java
* @Description: 支付、退款回调通知
* @Author: tanyp
**/
@RestController
@RequestMapping("/api/pay")
public class PayController {
@Autowired
private PayService payService;
@PostMapping("/payCallback")
public Wrapper payCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
return WrapMapper.ok(payService.payCallback(request, response));
}
@PostMapping("/refunCallback")
public Wrapper refunCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
return WrapMapper.ok(payService.refunCallback(request, response));
}
}
PayService.java
代码语言:javascript复制package com.tansci.service;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
public interface PayService {
Object payCallback(HttpServletRequest request, HttpServletResponse response) throws Exception;
Object refunCallback(HttpServletRequest request, HttpServletResponse response) throws Exception;
}
PayServiceImpl.java
代码语言:javascript复制package com.tansci.service.impl;
import com.alibaba.fastjson2.JSON;
import com.tansci.service.PayService;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.model.RefundNotification;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Objects;
import static com.wechat.pay.java.core.http.Constant.*;
@Slf4j
@Service
public class PayServiceImpl implements PayService {
// 商户号
private static String merchantId = "124589286";
// 商户证书序列号
private static String merchantSerialNumber = "3D3D4ADM154FDG44DFG45GF1SDF4JFA8DF95";
// 商户APIV3密钥
private static String apiV3Key = "yyh1466255d14dggh524dg666983286";
// 商户API私钥
private static String privateKey = "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDPH5wD/SO6 3jMUdUio0awAwR Ni7s22Csqk8EQLoYbeOzCN 4 bKJ4/jAuJ8CuqaCCCXtCDRlriLW35C7uzeuOiL3tN";
@Override
public Object payCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
//读取请求体的信息
ServletInputStream inputStream = request.getInputStream();
StringBuffer stringBuffer = new StringBuffer();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s;
//读取回调请求体
while ((s = bufferedReader.readLine()) != null) {
stringBuffer.append(s);
}
String s1 = stringBuffer.toString();
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
String nonce = request.getHeader(WECHAT_PAY_NONCE);
String signType = request.getHeader("Wechatpay-Signature-Type");
String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
// 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
// 没有的话,则构造一个
NotificationConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKey(privateKey)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(config);
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serialNo)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
.signType(signType)
.body(s1)
.build();
Transaction parse = parser.parse(requestParam, Transaction.class);
log.info("支付回调参数:{}", JSON.toJSON(parse));
try {
/**
* trade_state:
* SUCCESS:支付成功
* REFUND:转入退款
* NOTPAY:未支付
* CLOSED:已关闭
* REVOKED:已撤销(付款码支付)
* USERPAYING:用户支付中(付款码支付)
* PAYERROR:支付失败(其他原因,如银行返回失败)
*/
if ("SUCCESS".equals(parse.getTradeState().toString())) {
// 成功
log.info("==========支付回调【支付成功】=============");
} else if ("USERPAYING".equals(parse.getTradeState().toString())
|| "NOTPAY".equals(parse.getTradeState().toString())) {
// 未支付
log.info("==========支付回调【未支付】=============");
} else if ("PAYERROR".equals(parse.getTradeState().toString())
|| "REVOKED".equals(parse.getTradeState().toString())
|| "REFUND".equals(parse.getTradeState().toString())
|| "CLOSED".equals(parse.getTradeState().toString())) {
// 支付失败
log.info("==========支付回调【支付失败】=============");
}
} catch (Exception e) {
log.error("支付回调处理异常:{}", e);
return HttpStatus.INTERNAL_SERVER_ERROR;
}
log.info("-----------------------支付回调完成-----------------------");
return HttpStatus.OK;
}
@Override
public Object refunCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {
//读取请求体的信息
ServletInputStream inputStream = request.getInputStream();
StringBuffer stringBuffer = new StringBuffer();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s;
//读取回调请求体
while ((s = bufferedReader.readLine()) != null) {
stringBuffer.append(s);
}
String s1 = stringBuffer.toString();
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
String nonce = request.getHeader(WECHAT_PAY_NONCE);
String signType = request.getHeader("Wechatpay-Signature-Type");
String serialNo = request.getHeader(WECHAT_PAY_SERIAL);
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
// 如果已经初始化了 RSAAutoCertificateConfig,可直接使用
// 没有的话,则构造一个
NotificationConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKey(privateKey)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(config);
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serialNo)
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
.signType(signType)
.body(s1)
.build();
RefundNotification parse = parser.parse(requestParam, RefundNotification.class);
log.info("退款回调参数:{}", JSON.toJSON(parse));
try {
/**
* refund_status:
* SUCCESS:退款成功
* CLOSED:退款关闭
* ABNORMAL:退款异常,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往【商户平台—>交易中心】,手动处理此笔退款
*/
if ("SUCCESS".equals(parse.getRefundStatus().toString())) {
// 成功
log.info("==========退款回调【退款失败】=============");
} else if ("CLOSED".equals(parse.getRefundStatus().toString()) || "ABNORMAL".equals(parse.getRefundStatus().toString())) {
// 失败
log.info("==========退款回调【退款失败】=============");
}
} catch (Exception e) {
log.error("退款回调处理异常:{}", e);
return HttpStatus.INTERNAL_SERVER_ERROR;
}
log.info("-----------------------退款回调完成-----------------------");
return HttpStatus.OK;
}
}
小程序统一发送消息
微信发送模板消息