SpringBoot實現(xiàn)整合微信支付方法詳解
1.準備工作
1.1 數(shù)據(jù)庫表
這里涉及微信支付一共兩個表:
訂單表

支付記錄表

1.2 實體類
數(shù)據(jù)庫對應的實體類:
訂單表
@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 = "訂單狀態(tài)(0:未支付 1:已支付)")
private Integer status;
@ApiModelProperty(value = "邏輯刪除 1(true)已刪除, 0(false)未刪除")
private Boolean isDeleted;
@ApiModelProperty(value = "創(chuàng)建時間")
@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 = "交易狀態(tài)")
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 = "創(chuàng)建時間")
@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數(shù)據(jù)庫連接 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 #關聯(lián)的公眾號appid wx.pay.app_id=wx74862e0dfc69954 #商戶號 wx.pay.partner=155895011 #商戶key wx.pay.partnerkey=T6m9iK73b0kn9g5v426MKHQH7X8rKwb #回調(diào)地址 wx.pay.notifyurl=http://guli.shop/api/order/weixinPay/weixinNotify #微信提供的固定地址 wx.pay.wxurl=https://api.mch.weixin.qq.com/pay/unifiedorder #微信查詢狀態(tài)地址 wx.pay.queryUrl=https://api.mch.weixin.qq.com/pay/orderquery
1.5 創(chuàng)建讀取微信支付相關信息的工具類
創(chuàng)建一個讀取微信支付需要的信息的工具類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;
//定義公共靜態(tài)常量
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))); // 參數(shù)
http.setEntity(new UrlEncodedFormEntity(nvps, Consts.UTF_8)); // 設置參數(shù)
}
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();
// 響應內(nèi)容
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遠程調(diào)用其他模塊的方法。
詳細的Fegin的使用可以參考:SpringCloud-Feign遠程調(diào)用
2.1 遠程調(diào)用用戶模塊和課程模塊
在service_order訂單模塊創(chuàng)建:
@Component
@FeignClient("service-ucenter") //調(diào)用的服務名稱
public interface UcenterClient {
//根據(jù)用戶id獲取用戶信息,用于生成訂單使用
@PostMapping("/educenter/member/getUserInfoOrder/{id}")
public UcenterMemberOrder getUserInfoOrder(@PathVariable("id") String id);
}
@Component
@FeignClient("service-edu") //調(diào)用的服務名稱
public interface CourseClient {
//根據(jù)課程id查詢課程信息
@PostMapping("/eduservice/coursefront/getCourseInfoOrder/{id}")
public CourseWebOrder getCourseInfoOrder(@PathVariable("id") String id);
}
2.2 遠程調(diào)用方法的實現(xiàn)
在service-edu課程模塊實現(xiàn)根據(jù)課程id查詢課程信息的getCourseInfoOrder方法
controller層:
/**
* 根據(jù)課程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層:
/**
* 根據(jù)課程id,編寫sql語句查詢課程信息
* @param courseId 課程id
* @return CourseWebVo
*/
@Override
public CourseWebVo getBaseCourseInfo(String courseId) {
return baseMapper.getBaseCourseInfo(courseId);
}
mapper層:
<!--根據(jù)課程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用戶模塊實現(xiàn)根據(jù)用戶id獲取用戶信息的getUserInfoOrder方法
controller層:
/**
* 根據(jù)用戶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 根據(jù)課程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
//創(chuàng)建訂單,返回訂單號
String orderNo = orderService.createOrderById(courseId, JwtUtils.getMemberIdByJwtToken(request));
return R.ok().data("orderId", orderNo);
}
}
service層:
/**
* 根據(jù)courseId和userId生成訂單
*
* @param courseId 課程id
* @param userId 用戶id
* @return 返回訂單號
*/
@Override
public String createOrderById(String courseId, String userId) {
//通過遠程調(diào)傭根據(jù)用戶id獲取用戶信息
UcenterMemberOrder userInfoOrder = ucenterClient.getUserInfoOrder(userId);
//通過遠程調(diào)傭根據(jù)課程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());
//支付狀態(tài) 未支付:0 已支付:1
order.setStatus(0);
//支付類型 微信:1 支付寶:2
order.setPayType(1);
//保存到數(shù)據(jù)庫
baseMapper.insert(order);
//返回訂單號
return order.getOrderNo();
}
3.查詢訂單信息
3.1 controller層
在OrderController里創(chuàng)建getOrderInfo用于生成訂單:
/**
* 根據(jù)訂單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層
/**
* 根據(jù)訂單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里創(chuàng)建createNative用于生成支付二維碼:
@CrossOrigin
@RestController
@RequestMapping("/eduorder/paylog")
public class PayLogController {
@Autowired
private PayLogService payLogService;
/**
* 根據(jù)訂單號生成微信支付二維碼
* @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層
- 生成微信支付二維碼大概分為這幾步:
- 根據(jù)訂單號查詢訂單信息
- 使用map設置生成二維碼需要的參數(shù)
- 發(fā)送httpclient請求,傳遞xml格式的參數(shù),傳入微信支付提供的固定地址
- 得到發(fā)送請求返回的結果
- 最終返回封裝數(shù)據(jù)
/**
* 根據(jù)訂單號生成微信支付二維碼
* @param orderNo 訂單號
* @return map
*/
@Override
public Map createNative(String orderNo) {
try {
//1.根據(jù)訂單號查詢訂單信息
Order order = orderService.getOrderByOrderId(orderNo);
//2.使用map設置生成二維碼需要的參數(shù)
Map m = new HashMap();
//關聯(lián)的公眾號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.發(fā)送httpclient請求,傳遞參數(shù)xml格式,傳入微信支付提供的固定地址
HttpClient client = new HttpClient(ConstantWxPayUtils.WX_PAY_WX_URL);
//設置xml格式的參數(shù),需要傳入二維碼參數(shù)m和商戶key
client.setXmlParam(WXPayUtil.generateSignedXml(m, ConstantWxPayUtils.WX_PAY_PARTNER_KEY));
//默認不支持https,設置為true支持
client.setHttps(true);
//執(zhí)行請求發(fā)送
client.post();
//4.得到發(fā)送請求返回的結果
//返回的內(nèi)容是xml格式
String xml = client.getContent();
//把xml格式轉換為map集合
Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
//5.最終返回封裝數(shù)據(jù)
Map map = new HashMap();
//訂單號
map.put("out_trade_no", orderNo);
//課程id
map.put("course_id", order.getCourseId());
//價格
map.put("total_fee", order.getTotalFee());
//返回二維碼操作狀態(tài)碼
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.查詢訂單支付狀態(tài)
5.1 controller層
在PayLogController里創(chuàng)建queryPayStatus用于獲取支付狀態(tài):
/**
* 獲取支付狀態(tài)
* @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獲取訂單狀態(tài)
if(map.get("trade_state").equals("SUCCESS")){
//添加記錄到支付表,更新訂單表訂單狀態(tài)
payLogService.updateOrdersStatus(map);
return R.ok().message("支付成功!");
}
return R.ok().code(25000).message("正在支付中...");
}
5.2 service層
根據(jù)訂單號查詢訂單支付狀態(tài)大概分為一下幾步:
- 封裝參數(shù)
- 發(fā)送httpclient
- 得到請求返回的內(nèi)容
/**
* 根據(jù)訂單號查詢訂單支付狀態(tài)
* @param orderNo
* @return
*/
@Override
public Map<String, String> queryPayStatus(String orderNo) {
try {
//1.封裝參數(shù)
Map m=new HashMap();
//關聯(lián)的公眾號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.發(fā)送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.得到請求返回的內(nèi)容
String xml = client.getContent();
Map<String, String> resultMap=WXPayUtil.xmlToMap(xml);
return resultMap;
} catch (Exception e) {
e.printStackTrace();
throw new GuliException(20001,"查詢訂單支付狀態(tài)失敗");
}
}
如果支付成功,需要添加記錄到支付表,更新訂單表訂單狀態(tài):
/**
* 向支付表添加記錄,更新訂單表訂單狀態(tài)
* @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的訂單狀態(tài)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());
//支付狀態(tài)
payLog.setTradeState(map.get("trade_state"));
//交易流水號
payLog.setTransactionId(map.get("transaction_id"));
//其他屬性,轉為json字符串
payLog.setAttr(JSONObject.toJSONString(map));
baseMapper.insert(payLog);
}
以上就是SpringBoot實現(xiàn)整合微信支付方法詳解的詳細內(nèi)容,更多關于SpringBoot整合微信支付的資料請關注腳本之家其它相關文章!
- SpringBoot實現(xiàn)微信支付接口調(diào)用及回調(diào)函數(shù)(商戶參數(shù)獲取)
- java?Springboot對接開發(fā)微信支付詳細流程
- SpringBoot對接小程序微信支付的實現(xiàn)
- Springboot整合微信支付(訂單過期取消及商戶主動查單)
- UniApp?+?SpringBoot?實現(xiàn)微信支付和退款功能
- springboot對接微信支付的完整流程(附前后端代碼)
- 一篇文章帶你入門Springboot整合微信登錄與微信支付(附源碼)
- springboot整合微信支付sdk過程解析
- SpringBoot+MyBatis集成微信支付實現(xiàn)示例
相關文章
eclipse導入IntelliJ IDEA的maven項目的示例
本篇文章主要介紹了eclipse導入IntelliJ IDEA的maven項目的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-12-12
Java中的Random和ThreadLocalRandom詳細解析
這篇文章主要介紹了Java中的Random和ThreadLocalRandom詳細解析,Random 類用于生成偽隨機數(shù)的流, 該類使用48位種子,其使用線性同余公式進行修改,需要的朋友可以參考下2024-01-01

