com.github.wechatpay-apiv3
wechatpay-java
0.2.7
com.github.wechatpay-apiv3
wechatpay-apache-httpclient
0.4.9
官网引导:接入前准备-小程序支付 | 微信支付商户平台文档中心
①appid:小程序appid-登录小程序后台获取
②secret:小程序密钥-登录小程序后台获取
③appV3Secret:商户apiV3密钥-登录商户后台获取
④mchid:商户号-登录商户后台获取(商户号要绑定小程序)
⑤certpath:微信平台证书-登录商户后台获取
⑥privateKeyPath:商户私钥证书-登录商户后台获取
⑦merchantSerialNumber:商户证书序列化-登录商户后台获取
# application-dev.yml
wx:
miniapp:
configs:
- appid: wx1fcxxxxxxxx
secret: 1exxxxxxxxxxxxxxxxxxxxxxxxxxxxx
token: #微信小程序消息服务器配置的token
aesKey: #微信小程序消息服务器配置的EncodingAESKey
msgDataFormat: JSON
appV3Secret: ADxxxAas654xxxxxxxxxxxxxxxxxx # V3密钥
mchid: 12345678 # 商户号
certpath: D:/workspace/hxj-miniprogram-java/src/main/resources/wxpay/apiclient_cert.pem # 微信平台证书(发布时换成linux地址)
privateKeyPath: D:/workspace/hxj-miniprogram-java/src/main/resources/wxpay/apiclient_key.pem # 商户私钥证书(发布时换成linux地址)
payCallBackUrl: https://xxxxxxxxxxxxx/wechatPay/payCallback # 支付结束回调地址
timeExpire: 1200000 # 交易过期时间(单位毫秒)
merchantSerialNumber: 37ABCD098765ADCBF124656099AB # 商户证书序列号
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
/**
* @author xuhj
*/
@Slf4j
@Data
@ConfigurationProperties(prefix = "wx.miniapp")
public class WxMaProperties {
private List configs;
@Data
public static class Config {
/**
* 设置微信小程序的appid
*/
private String appid;
/**
* 设置微信小程序的Secret
*/
private String secret;
/**
* 设置微信小程序消息服务器配置的token
*/
private String token;
/**
* 设置微信小程序消息服务器配置的EncodingAESKey
*/
private String aesKey;
/**
* 消息格式,XML或者JSON
*/
private String msgDataFormat;
/**
* appV3Secret V3密钥
*/
private String appV3Secret;
/**
* mchid 商户号
*/
private String mchid;
/**
* 微信支付平台证书地址,注意 windows地址与linux地址不同
*/
private String certpath;
/**
* 商户API证书的路径(一般和上边的certpath在一起下载)
*/
private String privateKeyPath;
/**
* 支付回调地址
*/
private String payCallBackUrl;
/**
* 交易过期时间(单位毫秒)
*/
private Long timeExpire;
/**
* 商户证书序列号
*/
private String merchantSerialNumber;
}
}
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WxPayConfig {
@Autowired
private WxMaProperties properties;
/**
* 初始化商户配置(只能加载一次)
* 确保是单例
*
* @return
*/
@Bean
public RSAAutoCertificateConfig rsaAutoCertificateConfig() {
RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(properties.getConfigs().get(0).getMchid())
.privateKeyFromPath(properties.getConfigs().get(0).getPrivateKeyPath())
.merchantSerialNumber(properties.getConfigs().get(0).getMerchantSerialNumber())
.apiV3Key(properties.getConfigs().get(0).getAppV3Secret())
.build();
return config;
}
}
import com.wechat.pay.java.core.Config;
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.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.*;
import com.wechat.pay.java.service.payments.model.Transaction;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
import static com.wechat.pay.contrib.apache.httpclient.constant.WechatPayHttpHeaders.WECHAT_PAY_SIGNATURE;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_NONCE;
import static com.wechat.pay.java.core.http.Constant.WECHAT_PAY_TIMESTAMP;
/**
*
* 微信支付 前端控制器
*
*
* @author sunziwen
* @since 2023-03-23
*/
@Api(tags = {"微信支付"})
@Slf4j
@RestController
@RequestMapping("/wechatPay")
@Validated
public class PayController extends BaseController {
/**
* 配置文件
*/
@Autowired
private WxMaProperties properties;
@Autowired
private RSAAutoCertificateConfig rsaAutoCertificateConfig;
@Autowired
private IOrderMasterService iOrderMasterService;
@ApiOperation(value = "预支付-委托")
@PostMapping("preparePayment")
@ApiImplicitParams({
@ApiImplicitParam(name = "openid", value = "openid"),
@ApiImplicitParam(name = "description", value = "商品描述"),
@ApiImplicitParam(name = "outTradeNo", value = "商户订单号"),
@ApiImplicitParam(name = "total", value = "订单总金额,单位为分")
})
public ServiceResult> preparePayment(String openid, String description, String outTradeNo, Integer total) {
// 初始化服务
JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(rsaAutoCertificateConfig).build();
PrepayRequest request = new PrepayRequest();
// 应用ID
request.setAppid(properties.getConfigs().get(0).getAppid());
// 直连商户号
request.setMchid(properties.getConfigs().get(0).getMchid());
// 商品描述
request.setDescription(description);
// 商户订单号
request.setOutTradeNo(outTradeNo);
// 交易结束时间:2018-06-08T10:34:56+08:00
request.setTimeExpire(timeStampToRfc3339(System.currentTimeMillis() + properties.getConfigs().get(0).getTimeExpire()));
// 附加数据:在查询API和支付通知中原样返回,可作为自定义参数使用
request.setAttach("");
// 回调地址:异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 公网域名必须为https
request.setNotifyUrl(properties.getConfigs().get(0).getPayCallBackUrl());
// 商品标记,代金券或立减优惠功能的参数
request.setGoodsTag("");
// 限制支付类型
// request.setLimitPay(LimitPayEnum.);
// 电子发票入口开放标识:传入true时,支付成功消息和支付详情页将出现开票入口。需要在微信支付商户平台或微信公众平台开通电子发票功能,传此字段才可生效
request.setSupportFapiao(true);
Amount amount = new Amount();
amount.setTotal(total);
amount.setCurrency("CNY");
// 订单金额信息
request.setAmount(amount);
Payer payer = new Payer();
payer.setOpenid(openid);
// 支付者信息
request.setPayer(payer);
// 优惠功能
request.setDetail(null);
// 支付场景描述
request.setSceneInfo(null);
// 结算信息
request.setSettleInfo(null);
PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);
//重复支付判断TODO
// 存储预付订单信息 TODO
log.info("发起预支付成功-" + response.toString());
return success(response);
}
@ApiOperation(value = "预支付-回调(或退款)")
@RequestMapping("payCallback")
public synchronized String payCallback(HttpServletRequest request) throws IOException {
log.info("收到支付回调-------------");
// 请求头Wechatpay-nonce
String nonce = request.getHeader(WECHAT_PAY_NONCE);
// 请求头Wechatpay-Timestamp
String timestamp = request.getHeader(WECHAT_PAY_TIMESTAMP);
// 请求头Wechatpay-Signature
String signature = request.getHeader(WECHAT_PAY_SIGNATURE);
// 微信支付证书序列号 TODO 这里千万不能搞错,不能是商家证书序列号!!!
String serial = request.getHeader("Wechatpay-Serial");
// 签名方式
String signType = request.getHeader("Wechatpay-Signature-Type");
// 构造 RequestParam
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serial) // TODO 不能搞错!!!
.nonce(nonce)
.signature(signature)
.timestamp(timestamp)
.signType(signType)
.body(getRequestBody())
.build();
// 初始化 NotificationParser
NotificationParser parser = new NotificationParser(rsaAutoCertificateConfig);
// 以支付通知回调为例,验签、解密并转换成 Transaction
log.info("验签参数:" + requestParam);
Transaction transaction = parser.parse(requestParam, Transaction.class);
log.info("验签成功!-支付回调结果:" + transaction.toString());
// RefundNotification refundNotification = parser.parse(requestParam, RefundNotification.class);
// 修改订单之前,主动去微信支付主动查询订单是否已经支付成功,这一步不能少,防止有人假冒主动post接口。
/**
* 收到支付成功的回调:这里进行处理业务
*/
// List orderMasters = iOrderMasterService.lambdaQuery().eq(OrderMaster::getOrderNo, transaction.getOutTradeNo()).list();
// Assert.isTrue(orderMasters.size() == 1, "根据订单编号未查到订单OR查到了多个订单!!!");
// if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) {
// //支付成功 0待付款1待发货2待收货3已完成4已取消
// orderMasters.get(0).setBuyStatus(1);
// // 1待送仓2待打款3已完成4已取消
// orderMasters.get(0).setSaleStatus(1);
// // 1现金,2余额,3网银,4支付宝,5微信
// orderMasters.get(0).setPaymentMethod(5);
// return "{\"code\": \"FAIL\",\"message\": \"失败\"}";
// } else {
// log.info("支付未成功:" + transaction.getTradeState());
// }
// // TODO 存储支付流水
return "{\"code\": \"FAIL\",\"message\": \"失败\"}";
}
}