前言
都干到小程序了,那支付也避开不了,直接上号!
准备
首先必须接入微信支付,成为商家号,步骤可参考其他教程微信支付 - 中国领先的第三方支付平台 | 微信支付提供安全快捷的支付方式
流程
https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_4&index=3
网上方法千千万,就看能否找到属于你的另一半
以下是我的另一半,直接上代码
后端代码:
1、下单:
@PostMapping("/jsApiOrder")
@ApiOperation("JSAPI下单")
@Functional(visible = Visible.SIGNUP)
public ResultInfo<SortedMap<String, String>> jsApiOrder(@Validated @RequestBody OrderDto orderDto, HttpServletRequest request) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
AuthParam authParam = this.getLoginUser(request);
return new ResultInfo<>(wxPayService.jsApiOrder(orderDto, authParam));
}
//以下参数获取方法看准备步骤
@Value("${wx.pay.appId}")
private String appId;
@Value("${wx.pay.merchantId}")
private String merchantId;
//商户证书序列号
@Value("${wx.pay.merchantSerialNumber}")
private String merchantSerialNumber;
//商户APIv3密钥
@Value("${wx.pay.apiV3Key}")
private String apiV3Key;
//支付通知地址
@Value("${wx.pay.payNotifyUrl}")
private String payNotifyUrl;
//wx private key
private String WX_PRIVATE_KEY = "";
public static String privateKeyPath = "apiclient_key.pem";
//读取微信支付的私钥,我直接复制出来放在resources下
@PostConstruct
private void init() {
ClassPathResource classPathResource = new ClassPathResource(privateKeyPath);
try (InputStream inputStream = classPathResource.getInputStream()) {
StringBuilder privateKeyContent = new StringBuilder();
int len;
byte[] buffer = new byte[1024];
while ((len = inputStream.read(buffer)) != -1) {
privateKeyContent.append(new String(buffer, 0, len));
}
WX_PRIVATE_KEY = privateKeyContent.toString().replaceAll("\\n", "").trim();
} catch (IOException e) {
throw new IllegalArgumentException("WeiXin Private Key Read Err:" + e.getMessage());
}
log.info("WeiXin Private Key Read Success !!!");
}
/**
* 微信支付
*/
@Override
@Transactional
public SortedMap<String, String> jsApiOrder(OrderDto orderDto, AuthParam authParam) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException {
//判断是否有待支付订单
if(authParam != null){
String memberId = authParam.getUserId();
synchronized (memberId.intern()) {
Integer count = this.ordersMapper.selectByNotOrder1(memberId);
if(count > 0){
throw ErrCodeMsgEnum.SERVER_UPDATE_ERROR.err("有订单未支付,请先支付再继续下单");
}
}
}
//创建 PrepayRequest 对象并设置金额和付款人信息
PrepayRequest request = new PrepayRequest();
SortedMap<String, String> params = new TreeMap<>();
Amount amount = new Amount();
// 获取下单金额---需要去获取你当前下单的商品价格
String creationId = orderDto.getCreationId();
Creation creation = this.creationMapper.selectByCreationId(creationId);
Double creationPrice = creation.getCreationPrice();
// 将金额从元转换为分
int totalFeeInFen = (int) (creationPrice * 100);
amount.setTotal(totalFeeInFen);
amount.setCurrency("CNY");
Payer payer = new Payer();
//获取openId
String memberId = authParam.getUserId();
MemberInfo memberInfo = this.memberInfoMapper.selectInfoMemberId(memberId);
if(memberInfo != null){
payer.setOpenid(memberInfo.getOpenPid());
}
request.setAmount(amount);
request.setPayer(payer);
request.setAppid(appId);
request.setMchid(merchantId);
request.setDescription(orderDto.getDescription());
//设置支付结果通知URL 和订单号
request.setNotifyUrl(payNotifyUrl);
request.setOutTradeNo(generateOrderNumber("这个地方就是你订单编号的前缀,看怎么起"));
//调用微信支付系统 prepay 方法生成预支付订单
PrepayResponse response = getJsapiService().prepay(request);
//生成支付签名
WechatPaySign sign = sign(response.getPrepayId());
//构建返回参数
params.put("trans_no", generateOrderNumber("C2M"));
params.put("appId", appId);
params.put("nonceStr", sign.getNonceStr());
params.put("package", "prepay_id=" + sign.getPrepayId());
params.put("signType", "RSA");
params.put("timeStamp", sign.getTimeStamp());
params.put("paySign", sign.getSign());
//存储订单信息到数据库
Order order = new Order();
//这里就根据自己的表结构来了哈,还是轻松的
return params;
}
//获取支付签名
private WechatPaySign sign(String prepayId) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
//生成三十位随机数
String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 30; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
String nonceStr = sb.toString();//随机字符串
String packageStr = "prepay_id=" + prepayId;
// 不能去除'.append("\n")',否则失败
String signStr = appId + "\n" +
timeStamp + "\n" +
nonceStr + "\n" +
packageStr + "\n";
byte[] message = signStr.getBytes(StandardCharsets.UTF_8);
Signature sign = Signature.getInstance("SHA256withRSA");
sign.initSign(PemUtil.loadPrivateKeyFromString(WX_PRIVATE_KEY));
sign.update(message);
String signStrBase64 = Base64.getEncoder().encodeToString(sign.sign());
WechatPaySign wechatPaySign = new WechatPaySign();
wechatPaySign.setPrepayId(prepayId);
wechatPaySign.setTimeStamp(timeStamp);
wechatPaySign.setNonceStr(nonceStr);
wechatPaySign.setPackageStr(packageStr);
wechatPaySign.setSign(signStrBase64);
return wechatPaySign;
}
/**
* 创建小程序支付服务
*
* @return
*/
private JsapiService getJsapiService() {
Config config =
new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKey(WX_PRIVATE_KEY)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
config.createSigner().getAlgorithm();
return new JsapiService.Builder().config(config).build();
}
/**
* 生成订单编号
*
* @param prefix 订单编号前缀
* @return 生成的订单编号
*/
private synchronized String generateOrderNumber(String prefix) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String timestamp = dateFormat.format(new Date());
String key = String.format(Const.CacheKey.ORDER_NO, timestamp.substring(0, 8));
long number = this.redisCache.incr(key, 1);
this.redisCache.expire(key, DateUtils.getTomorrowSeconds());
String randomNum = String.format("%06d", number);
return prefix + timestamp + randomNum;
}
2、重新支付
这种情况就是用户下单后没有支付,那就需要重新拉取支付了
@PostMapping("/order/pay")
@ApiOperation("微信支付")
@Functional(visible = Visible.SIGNUP)
public ResultInfo<Map<String,String>> orderPay(@RequestParam("orderNumber") String orderNumber) {
return new ResultInfo<>(wxPayService.wxPay(orderNumber));
}
@Override
public Map<String, String> wxPay(String orderNumber) {
Order orderInfo = ordersMapper.selectByOrderNo(orderNumber);
Assert.notNull(orderInfo, "订单信息不存在");
Assert.isTrue(orderInfo.getOrderStatus().equals(OrderStatusEnum.WAIT_PAY.getCode()),
"订单状态异常,刷新后再试");
try {
//生成微信支付签名信息
WechatPaySign wxPaySign = sign(orderInfo.getPrepayId());
//返回结果
Map<String, String> resultMap = new HashMap<>();
resultMap.put("appId", appId);
resultMap.put("nonceStr", wxPaySign.getNonceStr());
resultMap.put("package", "prepay_id=" + wxPaySign.getPrepayId());
resultMap.put("signType", "RSA");
resultMap.put("timeStamp", wxPaySign.getTimeStamp());
resultMap.put("paySign", wxPaySign.getSign());
return resultMap;
} catch (NoSuchAlgorithmException|InvalidKeyException|SignatureException e) {
log.error("", e);
throw ErrCodeMsgEnum.WECHAT_PAY_ERROR.err();
}
}
3、回调
@PostMapping("/callback")
@ApiOperation("微信回调")
@Functional(visible = Visible.FREEDOM)
public ResultInfo<ResponseEntity> callback(HttpServletRequest request){
return new ResultInfo<>(wxPayService.callback(request));
}
@Transactional
public ResponseEntity callback(HttpServletRequest request) {
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(request.getHeader("Wechatpay-Serial"))
.nonce(request.getHeader("Wechatpay-Nonce"))
.timestamp(request.getHeader("Wechatpay-Timestamp"))
.signature(request.getHeader("Wechatpay-Signature"))
.body(getBodyUTF8(request))
.build();
log.debug("wxPay---------------requestParam:"+requestParam.toString());
NotificationConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(merchantId)
.privateKey(WX_PRIVATE_KEY)
.merchantSerialNumber(merchantSerialNumber)
.apiV3Key(apiV3Key)
.build();
// 创建 NotificationParser 并解析请求参数
NotificationParser parser = new NotificationParser(config);
Transaction transaction = parser.parse(requestParam, Transaction.class);
String outTradeNo = transaction.getOutTradeNo();
log.debug("--------------订单号:"+outTradeNo);
Order orderInfo = ordersMapper.selectByOrderNo(outTradeNo);
Assert.notNull(orderInfo, "订单信息不存在");
//log.debug("--------------orderInfo:"+orderInfo);
//修改状态这里
return ResponseEntity.status(HttpStatus.OK).build();
}