手把手教你如何基于Springboot对接微信支付接口_Native支付(奶爸级别)
Posted 胡金水
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手教你如何基于Springboot对接微信支付接口_Native支付(奶爸级别)相关的知识,希望对你有一定的参考价值。
微信开发文档地址:https://pay.weixin.qq.com/wiki/doc/api/index.html
申请商户号地址:https://pay.weixin.qq.com/static/help_guide/bind_guide_admin.shtml
demo下载地址:https://download.csdn.net/download/qq_17555933/18507085
微信对接时序图:
准备资料
# 微信支付二维码key
wxpay.qrcodeKey=wxpay_qrcode
# 微信支付二维码过期时间为 < 2小时(微信二维码code_url有效期为2小时)
wxpay.qrcodeExpire=7000
# 注意!!!如果异步通知接口没有返回success,微信异步通知频率为15/15/30/180/1800/1800/1800/1800/3600,单位:秒
# 公众账号ID
wxpay.appId=xxx
# 商户号
wxpay.merchantId=xxx
# 商户秘钥
wxpay.secrectKey=xxxx
# APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP
wxpay.spbillCreateIp=127.0.0.1
# 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数
wxpay.notifyUrl=http://5ktbak.natappfree.cc/payment/notice/wxpay
# 支付方式,取值如下:JSAPI,NATIVE,APP
wxpay.tradeType=NATIVE
# 微信支付 - 统一下单地址
wxpay.placeOrderUrl=https://api.mch.weixin.qq.com/pay/unifiedorder
代码实现
WXPayController.java
package com.wxpay.controller;
import com.wxpay.pojo.Orders;
import com.wxpay.pojo.PayResult;
import com.wxpay.pojo.PaymentInfoVO;
import com.wxpay.pojo.PreOrderResult;
import com.wxpay.resource.WXPayResource;
import com.wxpay.service.WxOrderService;
import com.wxpay.utils.DateUtil;
import com.wxpay.utils.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
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 javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* @author: HuGoldWater
* @description:
*/
@Slf4j
@RestController
@RequestMapping("payment")
public class WXPayController {
@Autowired
private WXPayResource wxPayResource;
@Autowired
private WxOrderService wxOrderService;
/**
* 支付成功后的微信支付异步通知
*/
@RequestMapping(value = "/notify/wxpay")
public void wxpay(HttpServletRequest request, HttpServletResponse response) throws Exception {
log.info("支付成功后的微信支付异步通知");
// 获取微信支付结果
PayResult payResult = wxOrderService.getWxPayResult(request.getInputStream());
boolean isPaid = payResult.getReturn_code().equals("SUCCESS") ? true : false;
// 查询该笔订单在微信那边是否成功支付
// 支付成功,商户处理后同步返回给微信参数
PrintWriter writer = response.getWriter();
if (isPaid) {
String merchantOrderId = payResult.getOut_trade_no(); // 商户订单号
String wxFlowId = payResult.getTransaction_id();
Integer paidAmount = payResult.getTotal_fee();
System.out.println("================================= 支付成功 =================================");
log.info("************* 支付成功(微信支付异步通知) - 时间: {} *************", DateUtil.getCurrentDateString(DateUtil.DATETIME_PATTERN));
log.info("* 商户订单号: {}", merchantOrderId);
log.info("* 微信订单号: {}", wxFlowId);
log.info("* 实际支付金额: {}", paidAmount);
log.info("*****************************************************************************");
// 通知订单服务,该订单已支付
System.out.println("================================= 通知订单服务,该订单已支付 =================================");
// 通知微信已经收到消息,不要再给我发消息了,否则微信会10连击调用本接口
String noticeStr = setXML("SUCCESS", "");
writer.write(noticeStr);
writer.flush();
} else {
System.out.println("================================= 支付失败 =================================");
// 支付失败
String noticeStr = setXML("FAIL", "");
writer.write(noticeStr);
writer.flush();
}
}
public static String setXML(String return_code, String return_msg) {
return "<xml><return_code><![CDATA[" + return_code + "]]></return_code><return_msg><![CDATA[" + return_msg + "]]></return_msg></xml>";
}
/**
* 微信扫码支付页面
*/
@PostMapping(value = "/getWXPayQRCode")
public JsonResult getWXPayQRCode(String merchantUserId, String merchantOrderId) throws Exception {
// 根据订单ID和用户ID查询订单详情
// Orders waitPayOrder = paymentOrderService.queryOrderByStatus(merchantOrderId,merchantUserId,PaymentStatus.WAIT_PAY.type);
Orders waitPayOrder = new Orders();
// 商品描述
String body = "胡金水-付款用户[" + merchantUserId + "]";
// 商户订单号
String out_trade_no = merchantOrderId;
// 使用map代替redis
Map<String, String> redisCacheMap = new HashMap<>();
// 从redis中去获得这笔订单的微信支付二维码,如果订单状态没有支付没有就放入,这样的做法防止用户频繁刷新而调用微信接口
if (waitPayOrder != null) {
String qrCodeUrl = redisCacheMap.get(wxPayResource.getQrcodeKey() + ":" + merchantOrderId);
if (StringUtils.isEmpty(qrCodeUrl)) {
// 订单总金额,单位为分
String total_fee = "1"; // 测试金额
// 统一下单
PreOrderResult preOrderResult = wxOrderService.placeOrder(body, out_trade_no, total_fee);
qrCodeUrl = preOrderResult.getCode_url();
}
PaymentInfoVO paymentInfoVO = new PaymentInfoVO();
paymentInfoVO.setAmount(1);
paymentInfoVO.setMerchantOrderId(merchantOrderId);
paymentInfoVO.setMerchantUserId(merchantUserId);
paymentInfoVO.setQrCodeUrl(qrCodeUrl);
redisCacheMap.put(wxPayResource.getQrcodeKey() + ":" + merchantOrderId, qrCodeUrl);
return JsonResult.ok(paymentInfoVO);
} else {
return JsonResult.errorMsg("该订单不存在,或已经支付");
}
}
}
WXPayResource.java
package com.wxpay.resource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "wxpay")
@PropertySource("classpath:wxpay.properties")
public class WXPayResource {
private String qrcodeKey;
private long qrcodeExpire;
private String appId;
private String merchantId;
private String secrectKey;
private String spbillCreateIp;
private String notifyUrl;
private String tradeType;
private String placeOrderUrl;
}
WxOrderServiceImpl.java
package com.wxpay.service.impl;
import com.wxpay.pojo.PayResult;
import com.wxpay.pojo.PreOrder;
import com.wxpay.pojo.PreOrderResult;
import com.wxpay.resource.WXPayResource;
import com.wxpay.service.WxOrderService;
import com.wxpay.utils.HttpUtil;
import com.wxpay.utils.Sign;
import com.wxpay.utils.XmlUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;
@Service
public class WxOrderServiceImpl implements WxOrderService {
@Autowired
private WXPayResource wxPayResource;
/**
* ==========================================
* 微信预付单:指的是在自己的平台需要和微信进行支付交易生成的一个微信订单,称之为“预付单”
* 订单:指的是自己的网站平台与用户之间交易生成的订单
* <p>
* 1. 用户购买产品 --> 生成网站订单
* 2. 用户支付 --> 网站在微信平台生成预付单
* 3. 最终实际根据预付单的信息进行支付
* ==========================================
*/
@Override
public PreOrderResult placeOrder(String body, String out_trade_no, String total_fee) throws Exception {
// 生成预付订单对象
PreOrder o = new PreOrder();
// 生成随机字符串
String nonce_str = UUID.randomUUID().toString().trim().replaceAll("-", "");
o.setAppid(wxPayResource.getAppId());
o.setBody(body);
o.setMch_id(wxPayResource.getMerchantId());
o.setNotify_url(wxPayResource.getNotifyUrl());
o.setOut_trade_no(out_trade_no);
if (total_fee != null && !total_fee.equals("")) {
o.setTotal_fee(Integer.parseInt(total_fee));
} else {
o.setTotal_fee(1);
}
o.setNonce_str(nonce_str);
o.setTrade_type(wxPayResource.getTradeType());
o.setSpbill_create_ip(wxPayResource.getSpbillCreateIp());
SortedMap<Object, Object> p = new TreeMap<Object, Object>();
p.put("appid", wxPayResource.getAppId());
p.put("mch_id", wxPayResource.getMerchantId());
p.put("body", body);
p.put("nonce_str", nonce_str);
p.put("out_trade_no", out_trade_no);
p.put("total_fee", total_fee);
p.put("spbill_create_ip", wxPayResource.getSpbillCreateIp());
p.put("notify_url", wxPayResource.getNotifyUrl());
p.put("trade_type", wxPayResource.getTradeType());
// 获得签名
String sign = Sign.createSign("utf-8", p, wxPayResource.getSecrectKey());
o.setSign(sign);
// Object转换为XML
String xml = XmlUtil.object2Xml(o, PreOrder.class);
// 统一下单地址
String url = wxPayResource.getPlaceOrderUrl();
// 调用微信统一下单地址
String returnXml = HttpUtil.sendPost(url, xml);
// XML转换为Object
PreOrderResult preOrderResult = (PreOrderResult) XmlUtil.xml2Object(returnXml, PreOrderResult.class);
return preOrderResult;
}
@Override
public PayResult getWxPayResult(InputStream inStream) throws Exception {
BufferedReader in = null;
String result = "";
in = new BufferedReader(new InputStreamReader(inStream));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
PayResult pr = (PayResult) XmlUtil.xml2Object(result, PayResult.class);
return pr;
}
}
MerchantOrdersBO.java
package com.wxpay.pojo;
import lombok.Data;
@Data
public class MerchantOrdersBO {
private String merchantOrderId; // 商户订单号
private String merchantUserId; // 商户方的发起用户的用户主键id
private Integer amount; // 实际支付总金额(包含商户所支付的订单费邮费总额)
private Integer payMethod; // 支付方式 1:微信 2:支付宝
private String returnUrl; // 支付成功后的回调地址(学生自定义)
}
Orders.java
package com.wxpay.pojo;
import lombok.Data;
import java.util.Date;
@Data
public class Orders {
/**
* 订单主键
*/
private String id;
/**
* 商户订单号
*/
private String merchantOrderId;
/**
* 商户方的发起用户的用户主键id
*/
private String merchantUserId;
/**
* 实际支付总金额(包含商户所支付的订单费邮费总额)
*/
private Integer amount;
/**
* 支付方式
*/
private Integer payMethod;
/**
* 支付状态 10:未支付 20:已支付 30:支付失败 40:已退款
*/
private Integer payStatus;
/**
* 从哪一端来的,比如从XXX过来的
*/
private String comeFrom;
/**
* 支付成功后的通知地址,这个是开发者那一段的,不是第三方支付通知的地址
*/
private String returnUrl;
/**
* 逻辑删除状态;1: 删除 0:未删除
*/
private Integer isDelete;
/**
* 创建时间(成交时间)
*/
private Date createdTime;
}
PaymentInfoVO.java
package com.wxpay.pojo;
import lombok.Data;
@Data
public class PaymentInfoVO {
private String merchantOrderId; // 商户订单号
private String merchantUserId; // 商户方的发起用户的用户主键id
private Integer amount; // 实际支付总金额(包含商户所支付的订单费邮费总额)
private String qrCodeUrl; // 二维码扫码地址
}
PayResult.java
package com.wxpay.pojo;
import lombok.Data;
/**
* 支付结果封装类
*/
@Data
public class PayResult {
private String return_code; // 返回状态码
private String appid; // 公众账号ID
private String mch_id; // 商户号
private String nonce_str; // 随机字符串
private String sign; // 签名
private String result_code; // 业务结果
private String openid; // 用户标识
private String trade_type; // 交易类型
private String bank_type; // 付款银行
private int total_fee; // 总金额
private int cash_fee; // 现金支付金额
private String transaction_id; // 微信支付订单号
private String out_trade_no; // 商户订单号
private String time_end; // 支付完成时间
private String return_msg; // 返回信息
private String device_info; // 设备号
private String err_code; // 错误代码
private String err_code_des; // 错误代码描述
private String is_subscribe; // 是否关注公众账号
private String fee_type; // 货币种类
private String cash_fee_type; // 现金支付货币类型
private String coupon_fee; // 代金券或立减优惠金额
private String coupon_count; // 代金券或立减优惠使用数量
private String coupon_id_$n; // 代金券或立减优惠ID
private String coupon_fee_$n; // 单个代金券或立减优惠支付金额
private String attach; // 商家数据包
}
PreOrder.java
package com.wxpay.pojo;
import lombok.Data;
/**
* 统一下单
*/
@Data
public class PreOrder {
private String appid;// 公众账号ID
private String mch_id;// 商户号
private String nonce_str;// 随机字符串
private String sign;// 签名
private String body;// 商品描述
private String out_trade_no;// 商户订单号
private int total_fee;// 订单总金额,单位为分
private String spbill_create_ip;// APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
private String notify_url;// 接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
private String trade_type;// 取值如下:JSAPI,NATIVE,APP
}
PreOrderResult.java
package com.wxpay.pojo;
import lombok.Data;
/**
* 微信支付 - 统一下单返回结果的封装entity
*/
@Data
public class PreOrderResult {
private String return_code以上是关于手把手教你如何基于Springboot对接微信支付接口_Native支付(奶爸级别)的主要内容,如果未能解决你的问题,请参考以下文章
手把手教你如何配置DBeaver对接FusionInsigth MRS Spark2x