SpringBoot实现整合微信支付方法详解

1.准备工作

1.1 数据库表

这里涉及微信支付一共两个表:

订单表

支付记录表

1.2 实体类

数据库对应的实体类:

订单表

@Data

@ToString

@EqualsAndHashCode(callSuper = false)

@Accessors(chain = true)

@TableName("t_order")

@ApiModel(value = "Order对象", description = "订单")

public class Order implements Serializable {

private static final long serialVersionUID = 1L;

@TableId(value = "id", type = IdType.ID_WORKER_STR)

private String id;

@ApiModelProperty(value = "订单号")

private String orderNo;

@ApiModelProperty(value = "课程id")

private String courseId;

@ApiModelProperty(value = "课程名称")

private String courseTitle;

@ApiModelProperty(value = "课程封面")

private String courseCover;

@ApiModelProperty(value = "讲师名称")

private String teacherName;

@ApiModelProperty(value = "会员id")

private String memberId;

@ApiModelProperty(value = "会员昵称")

private String nickname;

@ApiModelProperty(value = "会员手机")

private String mobile;

@ApiModelProperty(value = "订单金额(分)")

private BigDecimal totalFee;

@ApiModelProperty(value = "支付类型(1:微信 2:支付宝)")

private Integer payType;

@ApiModelProperty(value = "订单状态(0:未支付 1:已支付)")

private Integer status;

@ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除")

private Boolean isDeleted;

@ApiModelProperty(value = "创建时间")

@TableField(fill = FieldFill.INSERT)

private Date gmtCreate;

@ApiModelProperty(value = "更新时间")

@TableField(fill = FieldFill.INSERT_UPDATE)

private Date gmtModified;

}

支付日志表

@Data

@EqualsAndHashCode(callSuper = false)

@Accessors(chain = true)

@TableName("t_pay_log")

@ApiModel(value = "PayLog对象", description = "支付日志表")

public class PayLog implements Serializable {

private static final long serialVersionUID = 1L;

@TableId(value = "id", type = IdType.ID_WORKER_STR)

private String id;

@ApiModelProperty(value = "订单号")

private String orderNo;

@ApiModelProperty(value = "支付完成时间")

private Date payTime;

@ApiModelProperty(value = "支付金额(分)")

private BigDecimal totalFee;

@ApiModelProperty(value = "交易流水号")

private String transactionId;

@ApiModelProperty(value = "交易状态")

private String tradeState;

@ApiModelProperty(value = "支付类型(1:微信 2:支付宝)")

private Integer payType;

@ApiModelProperty(value = "其他属性")

private String attr;

@ApiModelProperty(value = "逻辑删除 1(true)已删除, 0(false)未删除")

private Boolean isDeleted;

@ApiModelProperty(value = "创建时间")

@TableField(fill = FieldFill.INSERT)

private Date gmtCreate;

@ApiModelProperty(value = "更新时间")

@TableField(fill = FieldFill.INSERT_UPDATE)

private Date gmtModified;

}

1.3 导入依赖

在订单模块service_order导入微信支付需要的依赖:

<dependencies>

<dependency>

<groupId>com.github.wxpay</groupId>

<artifactId>wxpay-sdk</artifactId>

<version>0.0.3</version>

</dependency>

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>fastjson</artifactId>

</dependency>

</dependencies>

1.4 配置文件

在配置文件application.properties配置相关的信息:

# 服务端口

server.port=8007

# 服务名

spring.application.name=service-order

# mysql数据库连接

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/guli?serverTimezone=GMT%2B8

spring.datasource.username=root

spring.datasource.password=root

#返回json的全局时间格式

spring.jackson.date-format=yyyy-MM-dd HH:mm:ss

spring.jackson.time-zone=GMT+8

#配置mapper xml文件的路径

mybatis-plus.mapper-locations=classpath:com/atguigu/eduorder/mapper/xml/*.xml

#mybatis日志

mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

# nacos服务地址

spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

#开启熔断机制

#feign.hystrix.enabled=true

# 设置hystrix超时时间,默认1000ms

#hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000

#关联的公众号appid

wx.pay.app_id=wx74862e0dfc69954

#商户号

wx.pay.partner=155895011

#商户key

wx.pay.partnerkey=T6m9iK73b0kn9g5v426MKHQH7X8rKwb

#回调地址

wx.pay.notifyurl=http://guli.shop/api/order/weixinPay/weixinNotify

#微信提供的固定地址

wx.pay.wxurl=https://api.mch.weixin.qq.com/pay/unifiedorder

#微信查询状态地址

wx.pay.queryUrl=https://api.mch.weixin.qq.com/pay/orderquery

1.5 创建读取微信支付相关信息的工具类

创建一个读取微信支付需要的信息的工具类ConstantWxPayUtils:

@Controller

public class ConstantWxPayUtils implements InitializingBean {

@Value("${wx.pay.app_id}")

private String appID;

@Value("${wx.pay.partner}")

private String partner;

@Value("${wx.pay.partnerkey}")

private String partnerKey;

@Value("${wx.pay.notifyurl}")

private String notifyUrl;

@Value("${wx.pay.wxurl}")

private String wxUrl;

@Value("${wx.pay.queryUrl}")

private String queryUrl;

//定义公共静态常量

public static String WX_PAY_APP_ID;

public static String WX_PAY_PARTNER;

public static String WX_PAY_PARTNER_KEY;

public static String WX_PAY_NOTIFY_URL;

public static String WX_PAY_WX_URL;

public static String WX_PAY_QUERY_URL;

@Override

public void afterPropertiesSet() throws Exception {

WX_PAY_APP_ID = appID;

WX_PAY_PARTNER = partner;

WX_PAY_PARTNER_KEY = partnerKey;

WX_PAY_NOTIFY_URL = notifyUrl;

WX_PAY_WX_URL = wxUrl;

WX_PAY_QUERY_URL=queryUrl;

}

}

1.6 其他工具类

用于随机生成订单号的工具类OrderNoUtil:

public class OrderNoUtil {

/**

* 获取订单号

* @return

*/

public static String getOrderNo() {

SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");

String newDate = sdf.format(new Date());

String result = "";

Random random = new Random();

for (int i = 0; i < 3; i++) {

result += random.nextInt(10);

}

return newDate + result;

}

}

HttpClient工具类:

/**

* http请求客户端

*

* @author xppll

*

*/

public class HttpClient {

private String url;

private Map<String, String> param;

private int statusCode;

private String content;

private String xmlParam;

private boolean isHttps;

public boolean isHttps() {

return isHttps;

}

public void setHttps(boolean isHttps) {

this.isHttps = isHttps;

}

public String getXmlParam() {

return xmlParam;

}

public void setXmlParam(String xmlParam) {

this.xmlParam = xmlParam;

}

public HttpClient(String url, Map<String, String> param) {

this.url = url;

this.param = param;

}

public HttpClient(String url) {

this.url = url;

}

public void setParameter(Map<String, String> map) {

param = map;

}

public void addParameter(String key, String value) {

if (param == null)

param = new HashMap<String, String>();

param.put(key, value);

}

public void post() throws ClientProtocolException, IOException {

HttpPost http = new HttpPost(url);

setEntity(http);

execute(http);

}

public void put() throws ClientProtocolException, IOException {

HttpPut http = new HttpPut(url);

setEntity(http);

execute(http);

}

public void get() throws ClientProtocolException, IOException {

if (param != null) {

StringBuilder url = new StringBuilder(this.url);

boolean isFirst = true;

for (String key : param.keySet()) {

if (isFirst)

url.append("?");

else

url.append("&");

url.append(key).append("=").append(param.get(key));

}

this.url = url.toString();

}

HttpGet http = new HttpGet(url);

execute(http);

}

/**

* set http post,put param

*/

private void setEntity(HttpEntityEnclosingRequestBase http) {

if (param != null) {

List<NameValuePair> nvps = new LinkedList<NameValuePair>();

for (String key : param.keySet())

nvps.add(new BasicNameValuePair(key, param.get(key))); // 参数

http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 设置参数

}

if (xmlParam != null) {

http.setEntity(new StringEntity(xmlParam, Consts.UTF_8));

}

}

private void execute(HttpUriRequest http) throws ClientProtocolException,

IOException {

CloseableHttpClient httpClient = null;

try {

if (isHttps) {

SSLContext sslContext = new SSLContextBuilder()

.loadTrustMaterial(null, new TrustStrategy() {

// 信任所有

public boolean isTrusted(X509Certificate[] chain,

String authType)

throws CertificateException {

return true;

}

}).build();

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(

sslContext);

httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)

.build();

} else {

httpClient = HttpClients.createDefault();

}

CloseableHttpResponse response = httpClient.execute(http);

try {

if (response != null) {

if (response.getStatusLine() != null)

statusCode = response.getStatusLine().getStatusCode();

HttpEntity entity = response.getEntity();

// 响应内容

content = EntityUtils.toString(entity, Consts.UTF_8);

}

} finally {

response.close();

}

} catch (Exception e) {

e.printStackTrace();

} finally {

httpClient.close();

}

}

public int getStatusCode() {

return statusCode;

}

public String getContent() throws ParseException, IOException {

return content;

}

}

2.生成订单

这里一共涉及service_order订单模块、service_ucenter用户模块、service-edu课程模块。

service_order使用Fegin远程调用其他模块的方法。

详细的Fegin的使用可以参考:SpringCloud-Feign远程调用

2.1 远程调用用户模块和课程模块

在service_order订单模块创建:

@Component

@FeignClient("service-ucenter") //调用的服务名称

public interface UcenterClient {

//根据用户id获取用户信息,用于生成订单使用

@PostMapping("/educenter/member/getUserInfoOrder/{id}")

public UcenterMemberOrder getUserInfoOrder(@PathVariable("id") String id);

}

@Component

@FeignClient("service-edu") //调用的服务名称

public interface CourseClient {

//根据课程id查询课程信息

@PostMapping("/eduservice/coursefront/getCourseInfoOrder/{id}")

public CourseWebOrder getCourseInfoOrder(@PathVariable("id") String id);

}

2.2 远程调用方法的实现

在service-edu课程模块实现根据课程id查询课程信息的getCourseInfoOrder方法

controller层:

/**

* 根据课程id查询课程信息

* @param id 客场id

* @return CourseWebOrder

*/

@PostMapping("getCourseInfoOrder/{id}")

public CourseWebOrder getCourseInfoOrder(@PathVariable("id") String id) {

CourseWebVo courseInfo = courseService.getBaseCourseInfo(id);

CourseWebOrder courseWebOrder = new CourseWebOrder();

BeanUtils.copyProperties(courseInfo, courseWebOrder);

return courseWebOrder;

}

service层:

/**

* 根据课程id,编写sql语句查询课程信息

* @param courseId 课程id

* @return CourseWebVo

*/

@Override

public CourseWebVo getBaseCourseInfo(String courseId) {

return baseMapper.getBaseCourseInfo(courseId);

}

mapper层:

<!--根据课程id查询课程基本信息-->

<select id="getBaseCourseInfo" resultType="com.atguigu.eduservice.entity.frontvo.CourseWebVo">

SELECT ec.id,

ec.`title`,

ec.`price`,

ec.lesson_num as lessonNum,

ec.cover,

ec.buy_count as buyCount,

ec.view_count as viewCount,

ecd.description,

et.id teacherId,

et.`name` AS teacherName,

et.intro,

et.avatar,

es1.id as subjectLevelOneId,

es1.`title` AS subjectLevelOne,

es2.id as subjectLevelTwoId,

es2.`title` AS subjectLevelTwo

FROM edu_course ec

LEFT JOIN edu_course_description ecd ON ec.id = ecd.id

LEFT JOIN edu_teacher et ON ec.`teacher_id` = et.`id`

LEFT JOIN edu_subject es1 ON ec.`subject_parent_id` = es1.`id`

LEFT JOIN edu_subject es2 ON ec.`subject_id` = es2.`id`

WHERE ec.id = #{courseId}

</select>

在service_ucenter用户模块实现根据用户id获取用户信息的getUserInfoOrder方法

controller层:

/**

* 根据用户id获取用户信息,用于生成订单使用

*

* @param id 用户id

* @return UcenterMemberOrder

*/

@PostMapping("getUserInfoOrder/{id}")

public UcenterMemberOrder getUserInfoOrder(@PathVariable("id") String id) {

UcenterMember member = memberService.getById(id);

UcenterMemberOrder memberOrder = new UcenterMemberOrder();

BeanUtils.copyProperties(member, memberOrder);

return memberOrder;

}

2.3 根据课程id和用户id生成订单

controller层:

@CrossOrigin

@RestController

@RequestMapping("/eduorder/order")

public class OrderController {

@Autowired

private OrderService orderService;

/**

* 生成订单的方法

*

* @param courseId 课程id

* @param request 用于获取用户id

* @return 返回订单号

*/

@PostMapping("createOrder/{courseId}")

public R saveOrder(@PathVariable("courseId") String courseId, HttpServletRequest request) {

//通过JWT工具类获取用户id

//创建订单,返回订单号

String orderNo = orderService.createOrderById(courseId, JwtUtils.getMemberIdByJwtToken(request));

return R.ok().data("orderId", orderNo);

}

}

service层:

/**

* 根据courseId和userId生成订单

*

* @param courseId 课程id

* @param userId 用户id

* @return 返回订单号

*/

@Override

public String createOrderById(String courseId, String userId) {

//通过远程调佣根据用户id获取用户信息

UcenterMemberOrder userInfoOrder = ucenterClient.getUserInfoOrder(userId);

//通过远程调佣根据课程id获取课程信息

CourseWebOrder courseInfoOrder = courseClient.getCourseInfoOrder(courseId);

Order order = new Order();

//订单号

order.setOrderNo(OrderNoUtil.getOrderNo());

order.setCourseId(courseId);

order.setCourseTitle(courseInfoOrder.getTitle());

order.setCourseCover(courseInfoOrder.getCover());

order.setTeacherName(courseInfoOrder.getTeacherName());

order.setTotalFee(courseInfoOrder.getPrice());

order.setMemberId(userId);

order.setMobile(userInfoOrder.getMobile());

order.setNickname(userInfoOrder.getNickname());

//支付状态 未支付:0 已支付:1

order.setStatus(0);

//支付类型 微信:1 支付宝:2

order.setPayType(1);

//保存到数据库

baseMapper.insert(order);

//返回订单号

return order.getOrderNo();

}

3.查询订单信息

3.1 controller层

在OrderController里创建getOrderInfo用于生成订单:

/**

* 根据订单id查询订单信息

* @param orderId 订单id

* @return 返回订单信息

*/

@GetMapping("getOrderInfo/{orderId}")

public R getOrderInfo(@PathVariable("orderId") String orderId) {

Order order=orderService.getOrderByOrderId(orderId);

return R.ok().data("item", order);

}

3.2 service层

/**

* 根据订单id查询订单信息

*

* @param orderId 订单id

* @return 返回订单信息

*/

@Override

public Order getOrderByOrderId(String orderId) {

LambdaQueryWrapper<Order> queryWrapper = new LambdaQueryWrapper<>();

queryWrapper.eq(Order::getOrderNo, orderId);

return baseMapper.selectOne(queryWrapper);

}

4.生成微信支付的二维码

4.1 controller层

在PayLogController里创建createNative用于生成支付二维码:

@CrossOrigin

@RestController

@RequestMapping("/eduorder/paylog")

public class PayLogController {

@Autowired

private PayLogService payLogService;

/**

* 根据订单号生成微信支付二维码

* @param orderNo 订单号

* @return R

*/

@GetMapping("createNative/{orderNo}")

public R createNative(@PathVariable("orderNo") String orderNo){

//返回信息,包含二维码地址,还有其他信息

Map map=payLogService.createNative(orderNo);

return R.ok().data(map);

}

}

4.2 service层

  1. 生成微信支付二维码大概分为这几步:
  2. 根据订单号查询订单信息
  3. 使用map设置生成二维码需要的参数
  4. 发送httpclient请求,传递xml格式的参数,传入微信支付提供的固定地址
  5. 得到发送请求返回的结果
  6. 最终返回封装数据

/**

* 根据订单号生成微信支付二维码

* @param orderNo 订单号

* @return map

*/

@Override

public Map createNative(String orderNo) {

try {

//1.根据订单号查询订单信息

Order order = orderService.getOrderByOrderId(orderNo);

//2.使用map设置生成二维码需要的参数

Map m = new HashMap();

//关联的公众号appid

m.put("appid", ConstantWxPayUtils.WX_PAY_APP_ID);

//商户号

m.put("mch_id", ConstantWxPayUtils.WX_PAY_PARTNER);

//随机字符串

m.put("nonce_str", WXPayUtil.generateNonceStr());

//课程标题

m.put("body", order.getCourseTitle());

//订单号

m.put("out_trade_no", orderNo);

//价格

m.put("total_fee", order.getTotalFee().multiply(new BigDecimal("100")).longValue() + "");

//支付的ip地址

m.put("spbill_create_ip", "127.0.0.1");

m.put("notify_url", ConstantWxPayUtils.WX_PAY_NOTIFY_URL);

m.put("trade_type", "NATIVE");

//3.发送httpclient请求,传递参数xml格式,传入微信支付提供的固定地址

HttpClient client = new HttpClient(ConstantWxPayUtils.WX_PAY_WX_URL);

//设置xml格式的参数,需要传入二维码参数m和商户key

client.setXmlParam(WXPayUtil.generateSignedXml(m, ConstantWxPayUtils.WX_PAY_PARTNER_KEY));

//默认不支持https,设置为true支持

client.setHttps(true);

//执行请求发送

client.post();

//4.得到发送请求返回的结果

//返回的内容是xml格式

String xml = client.getContent();

//把xml格式转换为map集合

Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);

//5.最终返回封装数据

Map map = new HashMap();

//订单号

map.put("out_trade_no", orderNo);

//课程id

map.put("course_id", order.getCourseId());

//价格

map.put("total_fee", order.getTotalFee());

//返回二维码操作状态码

map.put("result_code", resultMap.get("result_code"));

//二维码地址

map.put("code_url", resultMap.get("code_url"));

return map;

} catch (Exception e) {

throw new GuliException(20001, "生成微信支付二维码失败");

}

}

5.查询订单支付状态

5.1 controller层

在PayLogController里创建queryPayStatus用于获取支付状态:

/**

* 获取支付状态

* @param orderNo 订单号

* @return R

*/

@GetMapping("queryPayStatus/{orderNo}")

public R queryPayStatus(@PathVariable("orderNo") String orderNo){

Map<String, String> map=payLogService.queryPayStatus(orderNo);

if(map==null){

return R.error().message("支付出错!");

}

//如果map不为空,通过map获取订单状态

if(map.get("trade_state").equals("SUCCESS")){

//添加记录到支付表,更新订单表订单状态

payLogService.updateOrdersStatus(map);

return R.ok().message("支付成功!");

}

return R.ok().code(25000).message("正在支付中...");

}

5.2 service层

根据订单号查询订单支付状态大概分为一下几步:

  1. 封装参数
  2. 发送httpclient
  3. 得到请求返回的内容

/**

* 根据订单号查询订单支付状态

* @param orderNo

* @return

*/

@Override

public Map<String, String> queryPayStatus(String orderNo) {

try {

//1.封装参数

Map m=new HashMap();

//关联的公众号appid

m.put("appid",ConstantWxPayUtils.WX_PAY_APP_ID);

//商户号

m.put("mch_id",ConstantWxPayUtils.WX_PAY_PARTNER);

//订单号

m.put("out_trade_no",orderNo);

//随机字符串

m.put("nonce_str",WXPayUtil.generateNonceStr());

//2.发送httpclient

HttpClient client = new HttpClient(ConstantWxPayUtils.WX_PAY_QUERY_URL);

client.setXmlParam(WXPayUtil.generateSignedXml(m,ConstantWxPayUtils.WX_PAY_PARTNER_KEY));

client.setHttps(true);

client.post();

//3.得到请求返回的内容

String xml = client.getContent();

Map<String, String> resultMap=WXPayUtil.xmlToMap(xml);

return resultMap;

} catch (Exception e) {

e.printStackTrace();

throw new GuliException(20001,"查询订单支付状态失败");

}

}

如果支付成功,需要添加记录到支付表,更新订单表订单状态:

/**

* 向支付表添加记录,更新订单表订单状态

* @param map

*/

@Override

public void updateOrdersStatus(Map<String, String> map) {

//从map获取订单号

String orderNo = map.get("out_trade_no");

Order order = orderService.getOrderByOrderId(orderNo);

//更新订单表t_order的订单状态status

if(order.getStatus().intValue()==1){

return;

}

order.setStatus(1);

orderService.updateById(order);

//向支付表 t_pag_log 添加记录

PayLog payLog=new PayLog();

payLog.setOrderNo(orderNo);

payLog.setPayTime(new Date());

//支付类型

payLog.setPayType(1);

//支付金额

payLog.setTotalFee(order.getTotalFee());

//支付状态

payLog.setTradeState(map.get("trade_state"));

//交易流水号

payLog.setTransactionId(map.get("transaction_id"));

//其他属性,转为json字符串

payLog.setAttr(JSONObject.toJSONString(map));

baseMapper.insert(payLog);

}

以上就是SpringBoot实现整合微信支付方法详解的详细内容,更多关于SpringBoot整合微信支付的资料请关注其它相关文章!

以上是 SpringBoot实现整合微信支付方法详解 的全部内容, 来源链接: utcz.com/p/251754.html

回到顶部