接入微信支付API v3的两种方式

微信支付文档:https://pay.weixin.qq.com/wiki/doc/apiv3/index.shtml
在这里插入图片描述
本篇介绍两种对接方式。

第一种:根据官方文档自定义对接

定义微信支付服务

@Slf4j
@Component
@RefreshScope
public class WxPayService {//商户id@Value("${wechat.merchantId}")private String merchantId;//应用ID@Value("${wechat.appId}")private String appId;//商户API私钥@Value("${wechat.privateKey}")private String privateKey;//微信支付平台证书@Value("${wechat.cert}")private String payCertificate;//商户证书序列号@Value("${wechat.mchSerialNo}")private String certificateSn;//微信支付平台证书@Value("${wechat.notifyUrl}")private String notifyUrl;//key@Value("${wechat.apiV3Key}")private String apiV3Key;private CloseableHttpClient httpClient;/*** 初始化参数*/@PostConstructpublic void initData() {try {PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(privateKey));X509Certificate certificate = PemUtil.loadCertificate(new FileInputStream(payCertificate));List<X509Certificate> list = new ArrayList<>();list.add(certificate);httpClient = WechatPayHttpClientBuilder.create().withMerchant(merchantId, certificateSn, merchantPrivateKey).withWechatPay(list).build();} catch (FileNotFoundException e) {log.error(e.getMessage());}}/*** 获取预支付交易会话标识** @param order* @return* @throws Exception*/@Overridepublic String prepay(OrderInfo order) throws Exception {HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/app");httpPost.addHeader("Accept", "application/json");httpPost.addHeader("Content-type", "application/json; charset=utf-8");JSONObject amount = new JSONObject();//订单总金额,单位为分amount.put("total", order.getPayment());//CNY:人民币,境内商户号仅支持人民币amount.put("currency", "CNY");JSONObject param = new JSONObject();param.put("mchid", merchantId);param.put("appid", appId);//商品描述param.put("description", order.getDescription());//回调地址param.put("notify_url", notifyUrl);//商户订单号param.put("out_trade_no", order.getOrderId());param.put("amount", amount);log.info("prepay request=>{}", param);CloseableHttpResponse response = null;try {httpPost.setEntity(new StringEntity(param.toString(), "UTF-8"));response = httpClient.execute(httpPost);String bodyAsString = EntityUtils.toString(response.getEntity());JSONObject resBody = JSON.parseObject(bodyAsString);return resBody.getString("prepay_id");} finally {if (response != null) {response.close();}}}/*** 签名** @param time* @param prepayId* @param nonce* @return* @throws Exception*/@Overridepublic String signature(Long time, String prepayId, String nonce) throws Exception {PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(privateKey));String text = String.format("%s\n%s\n%s\n%s\n", appId, time / 1000, nonce, prepayId);log.info("signature text=>{}", text);return sign(text, merchantPrivateKey);}/*** 解码** @param associated* @param nonce* @param ciphertext* @return* @throws Exception*/@Overridepublic String decrypt(String associated, String nonce, String ciphertext) throws Exception {return new AesUtil(apiV3Key.getBytes(StandardCharsets.UTF_8)).decryptToString(associated.getBytes(StandardCharsets.UTF_8), nonce.getBytes(StandardCharsets.UTF_8), ciphertext);}/*** 关闭订单** @param orderId* @return* @throws Exception*/@Overridepublic Boolean close(Long orderId) throws Exception {String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s/close";HttpPost httpPost = new HttpPost(String.format(url, orderId));httpPost.addHeader("Accept", "application/json");httpPost.addHeader("Content-type", "application/json; charset=utf-8");JSONObject reqBody = new JSONObject();reqBody.put("mchid", merchantId);httpPost.setEntity(new StringEntity(reqBody.toString(), "UTF-8"));CloseableHttpResponse response = httpClient.execute(httpPost);log.info(response.toString());// 返回204则表示成功int statusCode = response.getStatusLine().getStatusCode();if (statusCode == HttpStatus.NO_CONTENT.value()) {return Boolean.TRUE;} else {return Boolean.FALSE;}}private String sign(String data, PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);//用私钥对信息生成数字签名Signature signature = Signature.getInstance("SHA256withRSA");signature.initSign(privateKey);signature.update(dataBytes);return Base64.getEncoder().encodeToString(signature.sign());}/*** 退款申请** @param order* @param reason* @return* @throws Exception*/@Overridepublic Boolean refund(OrderInfo order, String reason) throws Exception {HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/refund/domestic/refunds");httpPost.addHeader("Accept", "application/json");httpPost.addHeader("Content-type", "application/json; charset=utf-8");JSONObject amount = new JSONObject();//原订单金额amount.put("total", order.getPayment());//退款金额amount.put("refund", order.getPayment());//退款币种amount.put("currency", "CNY");JSONObject reqBody = new JSONObject();//退款结果回调urlreqBody.put("notify_url", notifyUrl);//商户订单号reqBody.put("out_trade_no", order.getOrderId());//商户退款单号reqBody.put("out_refund_no", order.getOrderId());//退款原因,非必填reqBody.put("reason", reason);reqBody.put("amount", amount);httpPost.setEntity(new StringEntity(reqBody.toString(), "UTF-8"));CloseableHttpResponse response = httpClient.execute(httpPost);log.info(response.toString());String bodyAsString = EntityUtils.toString(response.getEntity());JSONObject resBody = JSON.parseObject(bodyAsString);log.info(resBody.toString());return Boolean.TRUE;}
}

调用示例

/*** 创建微信订单** @param userId  用户id* @param request 请求参数* @return*/
@Override
@Transactional(rollbackFor = Exception.class)
public CreateOrderDTO createWxOrder(Long userId, CreateOrderRequest request) {//商品idLong goodsId = request.getGoodsId();//根据用户id,商品id和订单状态查询历史订单List<OrderInfo> list = orderMapper.query(new QueryOrderRequest().setUserId(userId).setGoodsId(goodsId).setStatus(OrderStatus.CODE_UNPAID));try {// 当不存在历史订单时新创建订单,否则获取历史订单继续支付OrderInfo order;if (CollectionUtils.isEmpty(list)) {order = generateOrder(userId, goodsId);} else {order = list.get(0);}//预支付标识String prepayId = order.getPrepayId();//随机字符串String nonce = RandomUtil.randomString(32);long now = order.getGmtCreate().getTime();//签名String signature = wxPayService.signature(now, prepayId, nonce);CreateOrderDTO dto = new CreateOrderDTO();dto.setPrepayId(prepayId);dto.setTimestamp(now / 1000);dto.setNoncestr(nonce);dto.setSign(signature);dto.setOrderId(order.getOrderId());return dto;} catch (Exception e) {throw new ServerException("创建订单失败");}
}/*** 生成订单信息** @param userId   用户id* @param goodsId  商品id* @return*/
private OrderInfo generateOrder(Long userId, Long goodsId) throws Exception {GoodsInfoDO goods = goodsMapper.getGoodsById(goodsId);if (null == goods) {throw new NotFoundException("商品不存在");}OrderInfo order = new OrderInfo();order.setUserId(userId);order.setGoodsId(goodsId);order.setOrderId(new IdWorker().nextId());order.setGmtCreate(new Date());order.setDescription(goods.getDescription());order.setPayment(goods.getPrice());// 微信预支付接口String prepayId = wxPayService.prepay(order);order.setPrepayId(prepayId);orderMapper.addOrder(order);return order;
}/*** 关闭订单** @param orderId 订单id* @return*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean closeOrder(Long orderId) throws Exception {OrderInfo info = orderMapper.getOrderById(orderId);if (info == null) {throw new NotFoundException("订单不存在");}if(info.getStatus() != OrderStatus.CODE_UNPAID) {    	throw new ServerException("状态不正确,不允许关闭");}info.setStatus(OrderStatus.CODE_CLOSED);boolean flag = orderMapper.updateByOrderId(info) > 0;log.info("更新订单[{}]状态结果:{}", info.getOrderId(), flag);if (flag) {// 调用微信接口关闭订单try {wxPayService.close(orderId);} catch (Exception e) {e.printStackTrace();}}return flag;
}/*** 发起退款申请** @param userId* @param orderId 订单号* @param reason 退款原因* @return*/
@Override
@Transactional(rollbackFor = Exception.class)
public Result<Boolean> refund(Long userId, Long orderId, String reason) {OrderInfo info = new OrderInfo();info.setOrderId(orderId);info.setUserId(userId);OrderInfo order = orderMapper.getOrderByIdAndUserId(info);if (order == null || order.getId() == 0) {throw new ServerException("订单未找到");}//申请退款info.setStatus(OrderStatus.CODE_REFUNDING);info.setMark(reason);boolean flag = orderMapper.updateByOrderId(info) > 0;if (flag) {log.info("发起退款,开始调用wxpay的接口...");flag = wxPayService.refund(info, reason);log.info("调用wxpay退款接口结果:{}", flag);}return flag;
}private static final String PAY_SUCCESS = "TRANSACTION.SUCCESS";
private static final String REFUND_SUCCESS = "REFUND.SUCCESS";/**
* 回调接口
* @param request
**/
@Override
public Result<Boolean> callback(NotifyRequest request) {NotifyResource resource = request.getResource();String summary = request.getSummary();String eventType = request.getEvent_type();JSONObject notify;try {String msg = wxPayService.decrypt(resource.getAssociated_data(), resource.getNonce(), resource.getCiphertext());notify = JSONObject.parseObject(msg);} catch (Exception e) {return Result.fail(e.getMessage());}TransactionSuccessNotify successNotify = notify.toJavaObject(TransactionSuccessNotify.class);log.warn("notify = {}", successNotify);// 标记订单状态Long orderId = successNotify.getOut_trade_no();OrderInfo order = orderMapper.getOrderById(orderId);if (order == null) {throw new NotFoundException("订单未找到");}switch (eventType) {    	case PAY_SUCCESS: // 支付回调order.setStatus(OrderStatus.CODE_PAID);orderMapper.updateByOrderId(order);return Result.success(Boolean.TRUE);case REFUND_SUCCESS: // 退款回调order.setStatus(OrderStatus.CODE_REFUNDED);orderMapper.updateByOrderId(order);return Result.success(Boolean.TRUE);default:log.error("不支持的通知: {}", eventType);throw new NotFoundException("不支持的通知");}
}
第二种:使用第三方库

引入必要的jar包
pom.xml


<dependency><groupId>com.github.wechatpay-apiv3groupId><artifactId>wechatpay-apache-httpclientartifactId><version>0.4.8version>
dependency>
<dependency><groupId>com.github.wechatpay-apiv3groupId><artifactId>wechatpay-javaartifactId><version>0.1.0version>
dependency><dependency><groupId>com.github.binarywanggroupId><artifactId>weixin-java-payartifactId><version>4.4.0version>
dependency>
<dependency><groupId>com.github.binarywanggroupId><artifactId>weixin-java-mpartifactId><version>4.4.0version>
dependency>
<dependency><groupId>com.github.binarywanggroupId><artifactId>weixin-java-miniappartifactId><version>4.4.0version>
dependency>

调用示例

/**
* 关闭订单
* @param orderId
**/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean closeOrder(Long orderId) throws WxPayException {OrderInfo order = wxOrderInfoMapper.selectByOrderId(orderId);if (order.getStatus() != OrderStatus.CODE_UNPAID) {throw new ServerException("当前状态不允许关闭");}order.setStatus(OrderStatus.CODE_CLOSED);Boolean res = wxOrderInfoMapper.updateStatusByOrderId(order);if (res) {// 调用微信的关闭订单接口wxPayService.closeOrderV3(orderId.toString());}return res;
}/**
* 预支付
*
* @param orderId
* @param openId
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Result<OrderPrepayInfoDTO> toPay(String orderId, String openId) {OrderInfo order = wxOrderInfoMapper.selectByOrderId(orderId);      JSONObject object = prePay(orderId, openId, order);OrderPrepayInfoDTO prepay = new OrderPrepayInfoDTO();prepay.setOrderId(orderId);prepay.setAppId(object.getString("appId"));prepay.setTimestamp(object.getString("timeStamp"));prepay.setPackageValue(object.getString("packageValue"));prepay.setSignType(object.getString("signType"));prepay.setPaySign(object.getString("paySign"));prepay.setNonceStr(object.getString("nonceStr"));return Result.success(prepay);
}/**
* 预支付订单
*
* @param orderId
* @param openId
* @param order
* @return
* @throws WxPayException
*/
private JSONObject prePay(String orderId, String openId, OrderInfo order) throws WxPayException {WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();payer.setOpenid(openId);request.setPayer(payer);WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();amount.setTotal(order.getPayment());amount.setCurrency("CNY");request.setAmount(amount);request.setDescription(order.getDescription());request.setOutTradeNo(orderId);log.info("预支付订单请求-->{}", request.toString());WxPayUnifiedOrderV3Result.JsapiResult orderV3 = wxPayService.createOrderV3(TradeTypeEnum.JSAPI, request);return JSON.parseObject(JSON.toJSONString(orderV3));
}/**
* 需要与退款回调分开写
* @param notify
**/
@Override
public Result<Boolean> payCallback(OriginNotifyResponse notify, HttpServletRequest request) {log.info("Wechat pay callback");try {WxPayOrderNotifyV3Result v3Result = wxPayService.parseOrderNotifyV3Result(jsonStrSort(notify), getSignatureHeader(request));WxPayOrderNotifyV3Result.DecryptNotifyResult result = v3Result.getResult();log.info("DecryptNotifyResult=>{}", result.toString());Long orderId = Long.valueOf(result.getOutTradeNo());OrderInfo order = wxOrderInfoMapper.selectByOrderId(orderId);if (order == null) {throw new ClientErrorException(ErrorCode.INVALID_ORDER);}String tradeState = result.getTradeState();log.info("tradeState=>{}", tradeState);if ("SUCCESS".equals(tradeState)) {// 修改订单状态为已支付order.setStatus(OrderStatus.CODE_PAID);// 付款时间order.setPayTime(new Date());wxOrderInfoMapper.updateStatusByOrderId(order);// 将分享置为【1-已购买】状态userService.updateUserShareStatus(order.getShareId(), 1);return Result.success(Boolean.TRUE);}} catch (WxPayException e) {log.error("WxPayException:", e);return Result.fail(ResponseCode.ERROR_SERVER_EXCEPTION, e.getCustomErrorMsg());}return Result.fail();
}/**
* 退款申请
* @param request 请求参数
* @param userId 用户id
**/
@Override
@Transactional(rollbackFor = Exception.class)
public Result<Boolean> refund(RefundOrderRequest request, Long userId) {OrderInfo order = wxOrderInfoMapper.getOrderByUserIdAndOrderId(userId, Long.valueOf(request.getOrderId()));if (order == null) {throw new ResourceNotFoundException(ErrorCode.ORDER_NOT_FOUND);}order.setReason(request.getReason());// 退款申请中order.setStatus(WXOrderStatus.CODE_REFUNDING);          Boolean res = wxOrderInfoMapper.updateStatusByOrderId(order);if (!res) {throw new ServerErrorException(ErrorCode.INTERNAL_SERVER_ERROR);}WxPayRefundV3Result result = toRefund(order);switch (result.getStatus()) {case "SUCCESS":log.info("退款成功");order.setStatus(WXOrderStatus.CODE_REFUNDED);wxOrderInfoMapper.updateStatusByOrderId(order);break;case "CLOSED":log.info("退款关闭");break;case "PROCESSING":log.info("退款处理中");break;case "ABNORMAL":log.info("退款异常");break;default:log.info("受理失败");break;}return new Result<>(Boolean.TRUE, messageUtil.getMessage(ErrorCode.REFUND_REQUEST_RECEIVED));       }   
}/**
* 调用微信退款服务
*
* @param order
* @throws WxPayException
*/
private WxPayRefundV3Result toRefund(OrderInfo order) throws WxPayException {WxPayRefundV3Request refundRequest = new WxPayRefundV3Request();refundRequest.setOutTradeNo(order.getOrderId().toString());refundRequest.setOutRefundNo(order.getOrderId().toString());WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();amount.setTotal(order.getPayment());amount.setCurrency("CNY");amount.setRefund(order.getPayment());refundRequest.setAmount(amount);// 退款回调refundRequest.setNotifyUrl(paymentConfig.getMa_refund_notifyUrl());WxPayRefundV3Result result = wxPayService.refundV3(refundRequest);log.info("toRefund result=>{}", result);return result;
}/**
* 需要与支付回调分开写,否则接收不到
**/
@Override
public Result<Boolean> refundCallback(OriginNotifyResponse notify, HttpServletRequest request) {try {WxPayRefundNotifyV3Result v3Result = wxPayService.parseRefundNotifyV3Result(jsonStrSort(notify), getSignatureHeader(request));WxPayRefundNotifyV3Result.DecryptNotifyResult result = v3Result.getResult();log.info("DecryptNotifyResult=>{}", result.toString());OrderInfo order = wxOrderInfoMapper.selectByOrderId(result.getOutTradeNo());if (order == null) {throw new ClientErrorException(ErrorCode.INVALID_ORDER);}String refundStatus = result.getRefundStatus();log.info("refundStatus=>{}", refundStatus);if ("SUCCESS".equals(refundStatus)) {// 修改订单状态为退款成功order.setStatus(WXOrderStatus.CODE_REFUNDED);wxOrderInfoMapper.updateStatusByOrderId(order);return Result.success();}} catch (WxPayException e) {log.error("WxPayException:", e);}return Result.fail();
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部