微信支付
h5支付文档: https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1
参考文档地址: https://pay.weixin.qq.com/wiki/doc/api/index.html
这里用的是v2接口 ,使用的是binary-wang 工具
链接: http://binarywang.com/
准备
wx:
pay:
app-id: wx1231231 #appId
mch-id: 123123123 #商户号
mch-key: wx321323 #密钥
key-path: classpath:/certs/xxx.p12 #证书
notify-url: http://192.168.2.2:2323/public/notify #支付成功回调地址
spbill_create_ip: 101.91.219.95 #ip, 好像服务器ip和客户ip都行
pom添加依赖
<!--微信支付-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java-pay-spring-boot-starter</artifactId>
<version>4.1.0</version>
</dependency>
配置类
@Configuration
@ConditionalOnClass(WxPayService.class)
@EnableConfigurationProperties(WxPayProperties.class)
@AllArgsConstructor
public class WxPayConfiguration {
private WxPayProperties properties;
@SneakyThrows
@Bean
@ConditionalOnMissingBean
public WxPayService wxService() {
WxPayConfig payConfig = new WxPayConfig();
payConfig.setAppId(StringUtils.trimToNull(this.properties.getAppId()));
payConfig.setMchId(StringUtils.trimToNull(this.properties.getMchId()));
payConfig.setMchKey(StringUtils.trimToNull(this.properties.getMchKey()));
payConfig.setSubAppId(StringUtils.trimToNull(this.properties.getSubAppId()));
payConfig.setSubMchId(StringUtils.trimToNull(this.properties.getSubMchId()));
payConfig.setKeyPath(StringUtils.trimToNull(this.properties.getKeyPath()));
// 可以指定是否使用沙箱环境
payConfig.setUseSandboxEnv(false);
WxPayService wxPayService = new WxPayServiceImpl();
wxPayService.setConfig(payConfig);
return wxPayService;
}
}
创建订单
// 创建订单.
public WxPayAppOrderResult wxPayCreateOrder(String tradeNo, BigDecimal price, String body) {
WxPayUnifiedOrderRequest orderRequest = getWxPayUnifiedOrderRequest(body, tradeNo, price, WxPayConstants.TradeType.APP);
try {
log.info("生成订单参数:[{}]", orderRequest);
return wxPayService.createOrder(orderRequest);
} catch (WxPayException e) {
log.error("微信支付失败!原因:", e);
throw new IllegalArgumentException("微信支付失败!原因:" + e.getMessage());
}
}
public WxPayUnifiedOrderRequest getWxPayUnifiedOrderRequest(String body, String tradeNo, BigDecimal price, String tradeType) {
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
//签名类型
orderRequest.setSignType(WxPayConstants.SignType.MD5);
//终端IP
// ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// assert requestAttributes != null;
// HttpServletRequest contextRequest = requestAttributes.getRequest();
// spbill_create_ip
// orderRequest.setSpbillCreateIp(IPUtil.getIpAddr(contextRequest));
orderRequest.setSpbillCreateIp(storageWxPayProperties.getSpbillCreateIp());
//商品描述 例如: 腾讯充值中心-QQ会员充值
orderRequest.setBody(body);
//商户订单号 商户系统内部的订单号,32个字符内、可包含字母
orderRequest.setOutTradeNo(tradeNo);
//回调地址
String wxPayPath = storageWxPayProperties.getNotifyUrl() + PayBusinessTypeConstant.DRIVER_PAY;
orderRequest.setNotifyUrl(wxPayPath);
//支付类型 APP MEWB .
orderRequest.setTradeType(tradeType);
// orderRequest.setOpenid(openId); //app支付不需要openId
orderRequest.setTotalFee(price.multiply(new BigDecimal(100)).intValue());
return orderRequest;
}
微信支付回调
@PostMapping("/notify/{type}")
@ResponseBody
@ApiOperation(value = "微信支付回调", notes = "微信支付回调")
public String notify(@PathVariable() String type, @RequestBody String xmlData) throws WxPayException {
log.info("type:{}, xmlData:{}", type, xmlData);
return wxPayService.notify(type, xmlData);
}
@Override
public String notify(String type, String xmlData) throws WxPayException {
WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);
notifyResult.checkResult(wxPayService, WxPayConstants.SignType.MD5, true);
PayNotifyBusinessService businessService = PayBusinessFactory.getBusinessService(type);
businessService.wxNofityHandler(xmlData, notifyResult);
return WxPayNotifyResponse.success("成功");
}
@Override
public void wxNofityHandler(String xmlData, WxPayOrderNotifyResult notifyResult) {
// 获取商户交易订单号
String outTradeNo = notifyResult.getOutTradeNo();
//TODO 这里也可以做一些 appId machId 交易金额等一些校验
TmOrderInfo orderInfo = tmOrderInfoService.getOrderInfoByJyNo(outTradeNo);
if (orderInfo == null) {
return;
}
if (StringUtils.equals(orderInfo.getOrderStatus(), WxPayConstants.WxpayTradeStatus.SUCCESS)) {
return;
}
if (StringUtils.equals(WxPayConstants.ResultCode.SUCCESS, notifyResult.getReturnCode())) {
// 支付成功逻辑处理
}
}
查询订单
@Override
public void wxPayQueryResultHandler(String jyNo) {
TmOrderInfo orderInfo = tmOrderInfoService.getOrderInfoByJyNo(jyNo);
if (orderInfo == null) {
return;
}
try {
WxPayOrderQueryResult wxPayOrderQueryResult = this.wxPayService.queryOrder("", jyNo);
if (StringUtils.equals(orderInfo.getOrderStatus(), WxPayConstants.WxpayTradeStatus.SUCCESS)) {
return;
}
if (StringUtils.equals(WxPayConstants.ResultCode.SUCCESS, wxPayOrderQueryResult.getReturnCode())) {
if (!StringUtils.equals(wxPayOrderQueryResult.getTradeState(), WxPayConstants.WxpayTradeStatus.SUCCESS)) {
// 查询结果不为成功时
// throw new IllegalArgumentException(wxPayOrderQueryResult.getTradeStateDesc());
return;
}
orderInfo.setOpenid(wxPayOrderQueryResult.getOpenid());
// 微信订单号
orderInfo.setTransactionId(wxPayOrderQueryResult.getTransactionId());
orderInfo.setXmlData(wxPayOrderQueryResult.getXmlString());
orderInfo.setTimeEnd(wxPayOrderQueryResult.getTimeEnd());
orderInfo.setTradeType(wxPayOrderQueryResult.getTradeType());
// 支付状态设置已支付
orderInfo.setOrderStatus(WxPayConstants.WxpayTradeStatus.SUCCESS);
orderInfo.setUpdateTime(new Date());
orderInfo.setRealPayMoney(wxPayOrderQueryResult.getCashFee() == null ? "0" : String.valueOf(wxPayOrderQueryResult.getCashFee()));
this.tmOrderInfoService.updateById(orderInfo);
String businessId = orderInfo.getBusinessId();
TmDriverTradeRecord driverTradeRecord = tmDriverTradeRecordService.getById(businessId);
if (driverTradeRecord == null) {
return;
}
driverTradeRecord.setIsSuccess(1);
driverTradeRecord.setPayTime(DateUtil.parse(wxPayOrderQueryResult.getTimeEnd()));
driverTradeRecord.setUpTime(new Date());
this.tmDriverTradeRecordService.updateById(driverTradeRecord);
}
} catch (WxPayException e) {
log.error("查询支付结果失败,", e);
throw new IllegalArgumentException("查询支付结果失败,请稍后重试.");
}
}
支付宝支付
h5支付文档: https://opendocs.alipay.com/open/54/106682?pathHash=9ef0d2c7
异步通知文档:https://opendocs.alipay.com/open/204/105301
查询订单文档: https://opendocs.alipay.com/isv/02ti0w
app支付文档: https://opendocs.alipay.com/open/54/106370?pathHash=183e43fd
准备
alipay:
app-id: 202101
public-key: xx
private-key: xx=
gateway: https://openapi.alipay.com/gateway.do
notify-url: http://xxx/storage/public/alipay/notify/
char-set: UTF-8
# 支付成功返回地址
return-url: http://xxx/app.html#/pages/payment/paymentDetail
# 取消支付返回地址
quit-url: http://xxx/app.html#/pages/payment/paymentDetail
pom 添加依赖
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.34.0.ALL</version>
</dependency>
配置类
@Configuration
public class AliPayConfig {
@Bean
public AlipayClient alipayClient(AlipayProperties alipayProperties) throws AlipayApiException {
AlipayConfig alipayConfig = new AlipayConfig();
//设置网关地址
alipayConfig.setServerUrl(alipayProperties.getGateway());
//设置应用ID
alipayConfig.setAppId(alipayProperties.getAppId());
//设置应用私钥
alipayConfig.setPrivateKey(alipayProperties.getPrivateKey());
//设置请求格式,固定值json
alipayConfig.setFormat(AlipayConstants.FORMAT_JSON);
//设置字符集
alipayConfig.setCharset(AlipayConstants.CHARSET_UTF8);
//设置支付宝公钥
alipayConfig.setAlipayPublicKey(alipayProperties.getPublicKey());
//设置签名类型
alipayConfig.setSignType(AlipayConstants.SIGN_TYPE_RSA2);
//构造client
return new DefaultAlipayClient(alipayConfig);
}
}
@Data
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayProperties {
/**
* 支付宝网关
*/
private String gateway;
private String appId;
private String publicKey;
private String privateKey;
private String charSet;
private String format;
private String signType;
private String notifyUrl;
private String returnUrl;
private String quitUrl;
}
app下单
// 支付宝 app
AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
model.setOutTradeNo(tmDriverTradeRecord.getJyNo());
model.setBody("收银支付");
model.setSubject("收银支付");
model.setTimeoutExpress("30m");
model.setTotalAmount(tmDriverTradeRecord.getPayAmount().toString());
model.setProductCode("DRIVER_PAY_1");
request.setBizModel(model);
String notifyUrl = alipayProperties.getNotifyUrl() + PayBusinessTypeConstant.DRIVER_PAY;
request.setNotifyUrl(notifyUrl);
request.setBizModel(model);
//这里和普通的接口调用不同,使用的是sdkExecute
try {
AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
//就是orderString 可以直接给客户端请求,无需再做处理。
if (response.isSuccess()) {
log.info("支付宝返回body,{}", response.getBody());
driverPayVo.setAliPayAppOrderResult(response.getBody());
driverPayVo.setPayAppOrderResult(response.getBody());
} else {
throw new IllegalArgumentException(response.getMsg());
}
} catch (AlipayApiException e) {
log.error("支付宝支付失败.", e);
throw new IllegalArgumentException("支付宝支付失败,原因:" + e.getMessage());
}
h5下单
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setOutTradeNo(tmDriverTradeRecord.getJyNo());
model.setBody("收银支付");
model.setSubject("收银支付");
model.setTimeoutExpress("30m");
model.setTotalAmount(tmDriverTradeRecord.getPayAmount().toString());
model.setProductCode("DRIVER_PAY_1");
request.setBizModel(model);
String notifyUrl = alipayProperties.getNotifyUrl() + PayBusinessTypeConstant.DRIVER_PAY;
request.setNotifyUrl(notifyUrl);
// 支付成功返回地址
request.setReturnUrl(alipayProperties.getReturnUrl() + "?recNo=" + tmDriverTradeRecord.getRecNo());
// 用户取消后返回地址.
if (StringUtils.isNotBlank(alipayProperties.getQuitUrl())) {
model.setQuitUrl(alipayProperties.getQuitUrl() + "?recNo=" + tmDriverTradeRecord.getRecNo());
}
request.setBizModel(model);
//这里和普通的接口调用不同,使用的是sdkExecute
try {
AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
//就是orderString 可以直接给客户端请求,无需再做处理。
if (response.isSuccess()) {
log.info("支付宝H5返回:{}", response.getBody());
driverPayVo.setPayAppOrderResult(response.getBody());
} else {
log.error("e{}", response);
throw new IllegalArgumentException("请求支付宝失败:" + response.getSubMsg());
}
} catch (AlipayApiException e) {
log.error("支付宝支付失败.", e);
throw new IllegalArgumentException("支付宝支付失败,原因:" + e.getMessage());
}
回调
@PostMapping("/notify/{type}")
@Operation(summary = "支付宝回调通知")
public String notify(@PathVariable String type, @RequestParam Map<String, String> params) {
log.info("type:{}, params:{}", type, params);
return alipayService.notify(type, params);
}
@Override
public String aliPayNofityHandler(Map<String, String> params) {
String result = "failure";
try {
//异步通知验签
boolean signVerified = AlipaySignature.rsaCheckV1(params,
alipayProperties.getPublicKey(),
AlipayConstants.CHARSET_UTF8,
AlipayConstants.SIGN_TYPE_RSA2);
if (!signVerified) {
// TODO 验签失败则记录异常日志,并在response中返回failure.
log.error("支付成功,异步通知验签失败!");
return result;
}
log.info("支付成功,异步通知验签成功!");
String outTradeNo = params.get("out_trade_no");
TmOrderInfo orderInfo = tmOrderInfoService.getOrderInfoByJyNo(outTradeNo);
if (orderInfo == null) {
return "success";
}
if (StringUtils.equals(orderInfo.getOrderStatus(), WxPayConstants.WxpayTradeStatus.SUCCESS)) {
return "success";
}
//2.判断 total_amount 是否确实为该订单的实际金额
String totalAmount = params.get("total_amount");
//3.校验通知中的 seller_id是否为 out_trade_no 这笔单据的对应的操作方
// String sellerId = params.get("seller_id");
// if (!sellerId.equals(config.getProperty("alipay.seller-id"))) {
// log.error("商家PID校验失败");
// return result;
// }
//4.验证 app_id 是否为该商家本身
String appId = params.get("app_id");
if (!appId.equals(alipayProperties.getAppId())){
log.error("app_id校验失败");
return result;
}
//在支付宝的业务通知中,只有交易通知状态为 TRADE_SUCCESS 或 TRADE_FINISHED 时,支付宝才会认定为买家付款成功
String tradeStatus = params.get("trade_status");
if (!"TRADE_SUCCESS".equals(tradeStatus) && !"TRADE_FINISHED".equals(tradeStatus)){
log.error("支付未成功");
return result;
}
// 支付成功 处理逻辑.
result = "success";
} catch (AlipayApiException e) {
e.printStackTrace();
}
return result;
}
查询订单
@Override
public void aliPayQueryResultHandler(String jyNo) {
TmOrderInfo orderInfo = tmOrderInfoService.getOrderInfoByJyNo(jyNo);
if (orderInfo == null) {
return;
}
if (StringUtils.equals(orderInfo.getOrderStatus(), WxPayConstants.WxpayTradeStatus.SUCCESS)) {
return;
}
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo(jyNo);
// 支付宝订单号
// model.setTradeNo("");
request.setBizModel(model);
try {
AlipayTradeQueryResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
System.out.println(response.getBody());
String tradeStatus = response.getTradeStatus();
if (!"TRADE_SUCCESS".equals(tradeStatus) && !"TRADE_FINISHED".equals(tradeStatus)){
log.error("查询结果, 支付未成功......" + tradeStatus);
return;
}
// 支付成功 处理逻辑.
} catch (AlipayApiException e) {
log.error("查询支付宝接口失败.", e);
}
}