1.背景

实际开发中用到微信支付的概率非常大,

至于为什么这里不必要我多少......

微信支付大体需要对接的核心接口有

其实大部分支付都是这些,就像上一节我们讲的支付宝支付一样

这里以常用的H5支付为例,其他的都是差不多的....

值得注意的时候,微信支付最常用的就是H5支付和JSAPI支付,这两者的主要区别在于

H5支付必须在非微信浏览器打开;

JSAPI支付只能在微信的浏览器打开;

微信支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/index.html

1.统一下单
2.查询订单
3.支付结果通知
4.申请退款
5.查询退款
6.下载对账单

2.需要准备的环境

微信支付目前还没有沙箱测试环境,要开开发支付功能必须要有企业资质申请公众号和商户号才可以

主要的是:

公众号appid

商户号

商户号的apiSercet密码

商户号的证书

商户号上绑定好的回调域名

3.开发步骤

3.1.统一下单

代码如下:

package com.ldp.user.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.ldp.user.common.constant.PayConstant;
import com.ldp.user.common.constant.PayEnum;
import com.ldp.user.common.constant.TestData;
import com.ldp.user.common.constant.WeChatConstant;
import com.ldp.user.common.exception.ParamException;
import com.ldp.user.common.exception.WeChatException;
import com.ldp.user.common.util.wechat.WXPayUtil;
import com.ldp.user.entity.bo.PayBO;
import com.ldp.user.entity.vo.PayVO;
import com.ldp.user.service.IPayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import java.util.Date;
import java.util.HashMap;
import java.util.Map; /**
* @Copyright (C) XXXXXX科技股份技有限公司
* @Author: lidongping
* @Date: 2021-01-07 18:47
* @Description:
*/
@Service
@Slf4j
public class WeChatPayService implements IPayService {
@Override
public String getPayType() {
return PayConstant.WECHAT;
} @Override
public PayVO getPayInfo(PayBO payBO) throws Exception {
// 根据订单号查询订单数据 和 支付账号数据
// 这里测试 直接将数据写在代码里
payBO.setAppId(TestData.WX_APP_ID)
.setMerchantId(TestData.WX_MERCHANT_ID)
.setApiSecretKey(TestData.WX_SECRET)
.setPayTitle("测试支付")
.setPayMoney(0.01)
.setProductNo("P001")
.setAsynchronousNotifyUrl("http://lidongping.free.idcfengye.com/api/async/wechat/payment")
.setOpenId("ozwLvwah3xtA93csvZTWr0wON_IU");
// 以下是封装微信支付数据
Map dic = new HashMap<String, String>();
dic.put("appid", payBO.getAppId());
dic.put("mch_id", payBO.getMerchantId());
String nonceStr = RandomUtil.randomString(30);
dic.put("nonce_str", nonceStr);
dic.put("body", payBO.getPayTitle());
dic.put("out_trade_no", payBO.getOrderNo());
String amount = NumberUtil.roundStr(payBO.getPayMoney() * 100, 0);
dic.put("total_fee", amount);
dic.put("spbill_create_ip", payBO.getUserIp());
dic.put("notify_url", payBO.getAsynchronousNotifyUrl());
// 生成的支付信息一小时内有效
dic.put("time_expire", DateUtil.format(DateUtil.offsetHour(new Date(), 1), "yyyyMMddHHmmss"));
// 根据类型请求支付
PayEnum payType = PayEnum.valueOf(payBO.getPayType());
switch (payType) {
case WECHAT_QR:
dic.put("trade_type", "NATIVE");
dic.put("product_id", payBO.getProductNo());
break;
case WECHAT_JSAPI:
dic.put("trade_type", "JSAPI");
if (StrUtil.isEmpty(payBO.getOpenId())) {
throw new ParamException("openid 为空");
}
dic.put("openid", payBO.getOpenId());
break;
case WECHAT_H5:
dic.put("trade_type", "MWEB");
break;
default:
throw new ParamException("没有对应的支付方式");
}
log.info("签名前:" + JSON.toJSONString(dic));
dic.put("sign", WXPayUtil.generateSignature(dic, payBO.getApiSecretKey()));
String dataXml = WXPayUtil.mapToXml(dic);
log.info("微信请求下单url:" + WeChatConstant.PAY_URL);
log.info("微信请求下单参数:" + dataXml);
String resp = HttpUtil.post(WeChatConstant.PAY_URL, dataXml, 60 * 1000);
log.info("微信请求下单返回:" + resp);
Map<String, String> resMap = WXPayUtil.xmlToMap(resp);
// 请求失败
if (!resMap.get("return_code").equalsIgnoreCase(WeChatConstant.SUCCESS)) {
throw new WeChatException(resMap.get("return_msg"));
}
// 下单失败
if (!resMap.get("result_code").equalsIgnoreCase(WeChatConstant.SUCCESS)) {
throw new WeChatException(resMap.get("err_code_des"));
}
// 校验支付返回结果签名
if (!checkSign(resMap, payBO.getApiSecretKey())) {
throw new WeChatException("验证签名失败");
}
// 处理返回结果
PayVO payVO = new PayVO();
payVO.setOrderNo(payBO.getOrderNo());
switch (payType) {
case WECHAT_QR:
payVO.setPayInfo(resMap.get("code_url"));
break;
case WECHAT_JSAPI:
Map jsapi = new HashMap<String, String>();
jsapi.put("appId", payBO.getAppId());
// 当前时间秒
jsapi.put("timeStamp", System.currentTimeMillis() / 1000 + "");
jsapi.put("nonceStr", nonceStr);
jsapi.put("package", "prepay_id=" + resMap.get("prepay_id"));
jsapi.put("signType", "MD5");
jsapi.put("paySign", WXPayUtil.generateSignature(jsapi, payBO.getApiSecretKey()));
payVO.setPayInfo(jsapi);
break;
default:
payVO.setPayInfo(resMap.get("mweb_url"));
}
return payVO;
} /**
* 微信响应数据签名检查
*
* @param map
* @param key
* @return
* @throws Exception
*/
private static boolean checkSign(Map<String, String> map, String key) throws Exception {
String sign = map.get("sign");
map.remove("sign");
String orign = WXPayUtil.generateSignature(map, key);
return orign.equalsIgnoreCase(sign);
}
}

3.2.异步通知

代码如下:

package com.ldp.user.controller;

import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.ldp.user.common.constant.TestData;
import com.ldp.user.common.constant.WeChatConstant;
import com.ldp.user.common.util.RedisUtil;
import com.ldp.user.common.util.wechat.WXPayUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest;
import java.util.Map; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 01/10 11:18
* @description
*/
@RestController
@Slf4j
public class WeChatController {
/**
* 注意:
* 1.这里是只支付宝微信xml的参数接收,微信最近也提供了json的方式,本质上都是一样的
* 2.xml参数回调文档:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_7&index=8
*
* @param request
* @return
* @throws Exception
*/
@PostMapping("/async/wechat/payment")
public String notifyPaymentResult(HttpServletRequest request) throws Exception {
log.info("wechat异步回调....");
String paramXml = ServletUtil.getBody(request);
Map<String, String> paramsMap = WXPayUtil.xmlToMap(paramXml);
//解析参数
String returnCode = paramsMap.get("return_code");
String tradeStatus = paramsMap.get("result_code");
String appid = paramsMap.get("appid");
String totalFee = paramsMap.get("total_fee");
String orderNo = paramsMap.get("out_trade_no");
if (!WeChatConstant.SUCCESS.equals(returnCode)) {
log.error("回调状态错误:returnCode={}", returnCode);
return WeChatConstant.RETURN_FAIL;
}
// TODO 这里实际开发时根据订单号获取apiSecret秘钥
String apiSecret = TestData.WX_SECRET;
// 验签
if (!WXPayUtil.isSignatureValid(paramsMap, apiSecret)) {
log.error("验签失败");
return WeChatConstant.RETURN_FAIL;
}
if (!WeChatConstant.SUCCESS.equals(tradeStatus)) {
log.error("支付状态失败:tradeStatus={}", tradeStatus);
return WeChatConstant.RETURN_FAIL;
}
// TODO 更新数据库订单数据和缓存 ... 这里略
// 这里只是模拟方放入缓存,便于前端查询订单 60 * 60 * 2L 表示缓存2小时
RedisUtil.set(orderNo, JSON.toJSON(paramsMap), 60 * 60 * 2L);
log.info("异步处理成功.............");
return WeChatConstant.RETURN_SUCCESS;
}
}

3.3.测试

获取微信支付信息

@Test
void getPayInfo() {
String url = urlLocal + "/userOrder/payInfo";
System.out.println("请求地址:" + url);
HttpRequest request = HttpUtil.createRequest(Method.GET, url);
Map<String, Object> map = new HashMap<>();
map.put("orderNo", "NO" + System.currentTimeMillis());
// 微信支付 wechat 支付宝 alipay
map.put("payCategory", "wechat");
// 201 jsapi支付 , 202 微信H5 101支付宝H5
map.put("payType", "202");
request.form(map);
System.out.println("请求参数:" + map);
request.header("X-Real_IP", "192.168.5.195");
request.setConnectionTimeout(60 * 1000);
String respone = request.execute().body();
System.out.println("响应结果:" + respone);
}

测试结果

请求地址:http://localhost:8080/api/userOrder/payInfo
请求参数:{payCategory=wechat, orderNo=NO1610250837438, payType=202}
响应结果:{"message":"success","code":100,"data":{"orderNo":"NO1610250837438","payInfo":"https://wx.tenpay.com/cgi-bin/mmpayweb-bin/checkmweb?prepay_id=wx10115509234051b333adbe01d032b80000&package=2821154835"}}

从响应的结果看,我们已经拿到了payInfo,也就是跳转到微信的url

正常情况下,只要在非微信浏览器打开就可以了,但是微信那边对请求来源的域名做了校验,我们这里测试不是很方便,就不继续演示下去了

因为实际开发中只要走到这一步,对于开发人员来说代码就已经合格了,如果只会支付不了,那多半是微信商户号上的配置有问题....

学习我们就到这里,如果在实际开发中有问题可以咨询我,共同交流技术!

4.总结

1.这里只是给大家演示了统一下单和支付回调的代码,也是微信支付的核心代码,相信只要这调通了,其他的接口类似!

2.为了更好让大家快速掌握微信支付,这篇博客已经做了配套的视频讲解,大家可以结合视频讲解学习,或者单独问我!

完美!

微信支付java版(含视频讲解)的更多相关文章

  1. 微信支付java版V3验证数据合法性

    [TOC] 1. 微信支付java版V3验证数据合法性 概要:使用微信支付接口时,微信会返回或回调给商户XML数据,开发者需要验证微信返回的数据是否合法. 特别提醒:商户系统对于支付结果通知的内容一定 ...

  2. 银联支付java版

    注:本文来源于:<  银联支付java版    > 银联支付java版 2016年09月18日 15:55:20 阅读数:2431 首先去银联官网注册测试支付账户   下载对应的demo[ ...

  3. 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_1-1.SpringBoot整合微信支付开发在线教育视频站点介绍

    笔记 第一章项目介绍和前期准备 1.SpringBoot整合微信支付开发在线教育视频站点介绍     简介: 课程介绍,和小D课堂在线教育项目搭建开发 1.课程大纲介绍         2.微信支付项 ...

  4. 微信支付.NET版开发总结(JS API),好多坑,适当精简

    前2天,做一个手机网页的微信支付的项目,费了好些周折,记录一下.接下来,按照开发步骤,细数一下,我遇到的那些坑. [坑1]官方邮件中下载的demo只有PHP版本,其他版本没有给链接.可能让人误以为只有 ...

  5. 微信支付.NET版开发总结(JS API),好多坑,适当精简。

    前2天,做一个手机网页的微信支付的项目,费了好些周折,记录一下.接下来,按照开发步骤,细数一下,我遇到的那些坑. [坑1]官方邮件中下载的demo只有PHP版本,其他版本没有给链接.可能让人误以为只有 ...

  6. Android微信支付V3版

    由于公司需求做微信APP支付,在集成过程中也遇到各种问题,比如说签名错误,body编码必须为UTF-8.APP端无法调用支付页面直接到支付结果页面.结果为null,code=-1等等: 1.签名错误问 ...

  7. app微信支付-java服务端接口 支付-查询-退款

    个人不怎么看得懂微信的文档,看了很多前辈的写法,终于调通了,在这里做一下记录. 首先来定义各种处理类(微信支付不需要特殊jar包,很多处理需要自己封装,当然也可以自己写完打个jar包) 参数要用jdo ...

  8. APP微信支付Java后台的实现(springmvc)

    第一次做微信支付,阅读完开发文档后,下了个官方demo,摸索了好久,期间也出现了好多问题,终于是实现生成预支付订单以及支付成功后接收微信服务器通知,不多说了,直接上代码: 一.工具类 Constant ...

  9. 微信支付java

    直接上代码: 1.支付配置PayCommonUtil import com.legendshop.payment.tenpay.util.MD5Util; import com.legendshop. ...

  10. 微信支付(java版本)_支付结果通知

    应用场景: 支付完成后,微信会把相关支付结果和用户信息发送给商户,商户需要接收处理,并返回应答. 对后台通知交互时,如果微信收到商户的应答不是成功或超时,微信认为通知失败,微信会通过一定的策略定期重新 ...

随机推荐

  1. Javascript高级程序设计第五章 | ch5 | 阅读笔记

    基本引用类型 Date 在不给定时间的情况下创建Date实例,创建的对象将保存当前的日期和时间. 要基于其他时间创建Date对象,必须传入其毫秒时表示 Date.parse() 月/日/年(5/21/ ...

  2. 第四届物联网与机器学习国际学术会议(IoTML 2024)

    [ACM独立出版,高录用,见刊检索快速稳定]第四届物联网与机器学习国际学术会议(IoTML 2024) [IoTML 2023会后三个半月内完成EI检索]2024 4th International ...

  3. json字符串转换对象或列表,多了字段不会报错

    json字符串转换对象或列表,多了字段不会报错 //DEMO1 转换对象 应用 riskId public class Item { private String id; private String ...

  4. uniapp ios推送 离线推送收不到消息

    突然之间收不到离线推送消息了,角标也不显示了. 查了很长时间发现是ios的推送证书过期了. 我用的是appuploader登陆上以后在证书管理中新创建证书就可以了.

  5. 容器镜像安全:安全漏洞扫描神器Trivy

    目录 一.系统环境 二.前言 三.Trivy简介 四.Trivy漏洞扫描原理 五.利用trivy检测容器镜像的安全性 六.总结 一.系统环境 本文主要基于Docker version 20.10.14 ...

  6. iOS登陆界面切换到注册界面并返回的UI设计(简易向)

    功能实现 从登陆界面进入注册界面 从注册界面返回登陆界面 功能实现思路 在网上搜了搜发现各位大神用的是navigation,但个人感觉没(zhen)大(ting)必(bu)要(dong).所以在这里提 ...

  7. Linux 内核:设备驱动模型(6)设备资源管理

    Linux 内核:设备驱动模型(6)设备资源管理 背景 不要总是用Linux 2.6的风格来写驱动代码了,也该与时俱进一下. 参考:http://www.wowotech.net/device_mod ...

  8. shell 根据 指定列 进行 去除 重复行

    根据指定列进行去除重复行 这里的重复是指如果两行的某一列数据相同,则认为是重复数据. 例如:第1行与第2行数据,其中的第2列(以- 作为分隔符)明显是重复的. 100069 - ARM Compile ...

  9. mysql求同比环比

    -- 参考:SQL计算月环比.月同比_路易吃泡面的博客-CSDN博客 -- mysql同比环比 drop table if EXISTS ordertable; create table ordert ...

  10. 数据结构—包(Bag)

    数据结构中的包,其实是对现实中的包的一种抽象. 想像一下现实中的包,比如书包,它能做什么?有哪些功能?首先它用来装东西,里面的东西可以随便放,没有规律,没有顺序,当然,可以放多个相同的东西.其次,东西 ...