1、开发文档

微信开发文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

安全规范:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3

1、签名算法
(签名校验工具)
签名生成的通用步骤如下: 第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。 特别注意以下重要规则: ◆ 参数名ASCII码从小到大排序(字典序);
◆ 如果参数的值为空不参与签名;
◆ 参数名区分大小写;
◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。 注意:密钥的长度为32个字节。

2、配置信息 和 工具类

2.1 核心配置文件

application.yml

#端口
server:
port: 8096 #微信支付自定义配置参数
wechat:
pay:
app-id: xxxx # 公众账号ID
mch-id: xxxx # 商户号ID
mch-key: xxxxx # 商户密钥
order-uri: https://api.mch.weixin.qq.com/pay/unifiedorder # 请求微信支付接口统一下单地址
notify-uri: http://xxxxx.natappfree.cc/wxpay/notifyresult # 微信支付结果回调地址
view-order-status-uri: https://api.mch.weixin.qq.com/pay/orderquery # 查询订单状态地址

注意:其中的回调地址,需要内网穿透;

2.2 自定义配置类

/**
* Created On : 3/11/2022.
* <p>
* Author : huayu
* <p>
* Description: 自定义配置类
*/
@Data
@Component
@ConfigurationProperties(prefix = "wechat.pay")
public class WechatPayConfig { /*
公众账号ID
*/
private String appId; /*
商户号ID
*/
private String mchId; /*
商户密钥
*/
private String mchKey; /*
请求微信支付接口统一下单地址
*/
private String orderUri; /*
微信支付结果回调地址
*/
private String notifyUri; /*
主动查询订单状态地址
*/
private String viewOrderStatusUri; }

2.3 常量类

/**
* Created On : 3/11/2022.
* <p>
* Author : huayu
* <p>
* Description: 微信支付常量类
*/
public class WechatPayConstant { //系统交易订单号的浅醉标识符
public static final String WECHAT_PAY_TRADE_ORDER_PREFIX ="kh"; //系统格式化日期字符串,到秒
public static final String WECHAT_PAY_TIME_PATTERN_ALL = "yyyyMMddHHmmss"; //系统格式化日期字符串,到天
public static final String WECHAT_PAY_TIME_PATTERN_DAY = "yyyyMMdd"; //微信支付的交易类型:naive
public static final String WECHAT_PAY_TRADE_NATIVE = "NATIVE"; //微信支付的签名方式MD5
public static final String WECHAT_PAY_SIGN_TYPE_MD5= "MD5"; //微信支付的签名参数名
public static final String WECHAT_PAY_FIELD_SIGN = "sign"; //微信支付下单请求的字符集编码
public static final String WECHAT_PAY_ENCODING_UTF8="UTF-8"; //微信支付下单结果的成功状态码
public static final String WECHAT_PAY_RESULT_SUCCESS = "SUCCESS"; //微信支付结果异步回调的同步响应,成功
public static final String WECHAT_PAY_NOTIFY_SUCCESS="<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>"; //微信支付结果异步回调的同步响应,失败
public static final String WECHAT_PAY_NOTIFY_FALL="<xml><return_code><![CDATA[FALL]]></return_code><return_msg><![CDATA[NOOK]]></return_msg></xml>"; //订单查询 结果成功的状态码
public static final String WECHAT_PAY_VIEW_ORDER_STATUS_SUCCESS = "SUCCESS"; }

2.4 工具类

WechatPayUtil 和 WechatPayXmlUtil :https://www.cnblogs.com/xiaoqigui/p/16855921.html

HttpClient4Util http请求工具类:https://www.cnblogs.com/xiaoqigui/p/16839536.html

3、请求微信统一下单接口,下单支付订单,返回支付链接

  1. 获取参数,放进map集合中并按key值,字典排序。
  2. 通过参数生成签名(生成的签名也放进map集合)。
  3. 将map集合转成xml字符串。
  4. 获取微信支付统一下单地址,xml参数字符串作为参数发送请求。
  5. 返回支付的链接(可以生成二维码给用户扫码支付)。

3.0 参数列表

3.1 接口

/**
* Created On : 3/11/2022.
* <p>
* Author : huayu
* <p>
* Description: 微信支付业务接口
*/
public interface WechatPayService {
/**
* @author : huayu
* @date : 3/11/2022
* @param : []
* @return : java.lang.String
* @description : 生成请求微信支付接口的统一下单接口参数 ,返回xml格式字符串
*/
String generateWxUnifyOrderXmlParams(String productBody,int totalFee) throws Exception; /**
* @author : huayu
* @date : 3/11/2022
* @param : [unifyOrderXmlParams]
* @return : java.util.Map<java.lang.String,java.lang.String>
* @description : 请求微信支付统一下单
*/
Map<String,String> getWchatPayUnifyOrderResult(String unifyOrderXmlParams) throws Exception; }

3.2 实现类

/**
* Created On : 3/11/2022.
* <p>
* Author : huayu
* <p>
* Description: 微信支付业务接口实现类
*/
@Service
@Slf4j
public class WechatPayServiceImpl implements WechatPayService { @Autowired
private WechatPayConfig wechatPayConfig; @Override
public String generateWxUnifyOrderXmlParams(String productBody,int totalFee) throws Exception {
//微信支付接口签名参数要求
//特别注意一下要求规则:参数名ASCII码从小到大排序(字典排序);如果参数值为空不参与签名;参数区分大小写; //定义微信支付解析参数集合:TreeMap(自动横距集合中key按照字典排序)
TreeMap<String, String> paramsMap = new TreeMap<>(); //公众账号ID appid 必传参数 微信支付分配的公众账号ID(企业号corpid即为此appid)
paramsMap.put("appid",wechatPayConfig.getAppId()); //商户号 mch_id 微信支付分配的商户号
paramsMap.put("mch_id",wechatPayConfig.getMchId()); //设备号 device_info 自定义参数,可以为终端设备号(门店号或收银设备ID),PC网页或公众号内支付可以传"WEB"
paramsMap.put("device_info","WEB"); //随机字符串 nonce_str 随机字符串,长度要求在32位以内。推荐随机数生成算法
paramsMap.put("nonce_str",WechatPayUtil.generateNonceStr()); //商品描述 body 商品简单描述,该字段请按照规范传递,具体请见参数规定
paramsMap.put("body",productBody); //附件数据 attach
paramsMap.put("attach","课工场KH96"); //商户订单号 out_trade_no 商户系统内部订单号,要求32个字符内(最少6个字符),只能是数字、大小写字母_-|*且在同一个商户号下唯一。详见商户订单号
paramsMap.put("out_trade_no",WechatPayUtil.generateTradeOrderNo()); //标价金额 total_fee 订单总金额,单位为分,详见支付金额
paramsMap.put("total_fee",String.valueOf(totalFee)); //终端IP spbill_create_ip 支持IPV4和IPV6两种格式的IP地址。用户的客户端IP
paramsMap.put("spbill_create_ip","121.225.201.22"); //通知地址 notify_url body 异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 公网域名必须为https,如果是走专线接入,使用专线NAT IP或者私有回调域名可使用http
paramsMap.put("notify_url",wechatPayConfig.getNotifyUri()); //交易类型 trade_type NATIVE -Native 支付 JSAPI -JSAPI支付 NATIVE -Native支付 APP -APP支付
paramsMap.put("trade_type", WechatPayConstant.WECHAT_PAY_TRADE_NATIVE); //商品ID trade_type=NATIVE时,此参数必传。此参数为二维码中包含的商品ID,商户自行定义。
paramsMap.put("product_id","互联网架构师"); //签名,传送的sign参数不参与签名,会将生成的签名与该sign值作校验。如果不一致,签名失败
//切记:签名都是再确定好接口参数后,指定签名操作,并将签名的结果加入参数集合
paramsMap.put("sign",WechatPayUtil.generateSignature(paramsMap,wechatPayConfig.getMchKey())); return WechatPayUtil.generateMapToXml(paramsMap);
} @Override
public Map<String, String> getWchatPayUnifyOrderResult(String unifyOrderXmlParams) throws Exception { //发送post请求,请求微信支付统一下单接口,下预支付订单,并获取下单结果
String unifyOrderXmlResult = HttpClient4Util.getResponse4PostByString(wechatPayConfig.getOrderUri()
, unifyOrderXmlParams
, WechatPayConstant.WECHAT_PAY_ENCODING_UTF8); return WechatPayUtil.generateXmlToMap(unifyOrderXmlResult);
} }

3.3 请求方法

/**
* Created On : 3/11/2022.
* <p>
* Author : huayu
* <p>
* Description: 微信支付接口对接测试入口
*/
@Slf4j
@RestController
public class WechatPagController { @Autowired
private WechatPayService wechatPayService; /**
* @author : huayu
* @date : 3/11/2022
* @param : []
* @return : com.kgc.scd.uitl.RequestResult<java.util.Map<java.lang.String,java.lang.String>>
* @description : 请求微信统一下单接口,下单支付订单,返回支付链接
*/
@PostMapping("/wxpay/unifyOrder")
public RequestResult<Map<String,String>> wechatPagUnifyOrder(@RequestParam String productBody,
@RequestParam int totalFree) throws Exception { //调用业务接口,生成微信支付接口,统一下单的完整参数,xml格式(微信支付的接口要求,交互参数必须是xml)
String unifyOrderXmlParams = wechatPayService.generateWxUnifyOrderXmlParams(productBody, totalFree); log.info("------ 1.请求微信支付统一下单参数:{} -------",unifyOrderXmlParams); //调用业务接口,请求微信支付统一下单接口,获取下单结果
Map<String, String> unifyOrderMapResult = wechatPayService.getWchatPayUnifyOrderResult(unifyOrderXmlParams); log.info("------ 2.请求微信支付统一下订单接口返回:{} --------",unifyOrderMapResult); //定义当前接口返回的map集合
Map<String, String> resultMap = new HashMap<>(); //解析微信支付下单结果,如果下单成功,获取用户进行扫码支付的连接,返回给前端,生成二维码,
if(WechatPayConstant.WECHAT_PAY_RESULT_SUCCESS.equals(unifyOrderMapResult.get("return_code"))
&& WechatPayConstant.WECHAT_PAY_RESULT_SUCCESS.equals(unifyOrderMapResult.get("result_code"))){
// 微信支付统一下单成功,省略签名校验 // 获取成功的结果
// 交易类型
resultMap.put("trade_type", unifyOrderMapResult.get("trade_type")); // 预支付交易会话标识
resultMap.put("prepay_id", unifyOrderMapResult.get("prepay_id")); // 二维码链接
resultMap.put("code_url", unifyOrderMapResult.get("code_url")); // 返回统一下预支付单成功
return ResultBuildUtil.success(resultMap); }
// 统一下预支付单失败
return ResultBuildUtil.fail(unifyOrderMapResult.get("return_code"), unifyOrderMapResult.get("return_msg")); } }

3.4 请求测试

3.4.1 发送请求

3.4.2 生成二维码

4、接收用户支付成功后,微信异步回调支付结果

  1. 支付后,微信官方,通过回调地址,返回用户信息通过数据流。
  2. 解析回调数据流。
  3. 解析异步回调的支付结果。
  4. 同步给微信官方响应结果。

4.1接口

/**
* @author : huayu
* @date : 3/11/2022
* @param : [wxpayNotifyXmlResult]
* @return : java.lang.String
* @description : 调用业务接口,解析异步回调的支付结果,并获取通知微信官方的结果
*/
String getNotifyWechatXmlResult(String wxpayNotifyXmlResult) throws Exception;

4.2 实现类

@Override
public String getNotifyWechatXmlResult(String wxpayNotifyXmlResult) throws Exception { //将微信支付结果通知的xml格式字符串,转换位map集合,方便处理
Map<String, String> wxpayNotifyXmlForMapResult = WechatPayUtil.generateXmlToMap(wxpayNotifyXmlResult); //解析回调结果
if(WechatPayConstant.WECHAT_PAY_RESULT_SUCCESS.equals(wxpayNotifyXmlForMapResult.get("return_code"))){
//官方提醒:商户系统对于支付结果通知的内容一定要做签名验证,并校验返回的订单金额是否与商户侧的订单金额一致,防止数据泄露导致出现“假通知”,造成资金损失。
if(WechatPayUtil.isSignatureValid(wxpayNotifyXmlForMapResult,wechatPayConfig.getMchKey())){
// 签名校验成功,说明回调结果是可信的,就可以进行业务处理,如果签名失败,说明回调来源不可信,不能进行业务处理
// TODO 真实业务中,收到正确回调,就要进行对应的业务处理,比如修改订单状态,发送主题消息,给定用户加积分,给物流生成物流单等等
// 业务处理成功后,需要同步给微信响应结果-成功 //成功
return WechatPayConstant.WECHAT_PAY_NOTIFY_SUCCESS; } } // 同步给微信响应结果-失败
return WechatPayConstant.WECHAT_PAY_NOTIFY_FALL;
}

4.3 请求方法

/**
* @author : huayu
* @date : 3/11/2022
* @param : [request, response]
* @return : void
* @description : 接收用户支付成功后,微信异步回调支付结果
*/
@RequestMapping("/wxpay/notifyresult")
public void receiveWechatPayNotifyResult(HttpServletRequest request, HttpServletResponse response){ //支付完成后,微信会把相关支付结果及用户信息通过数据流的形式发送给商户,商户需要接收处理,并按文档规范返回应答。 try(InputStream inputStream = request.getInputStream()){ //解析回调数据流
BufferedReader brf = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); //定义可变字符对象,保存返回的xml格式支付结果
StringBuilder wxpayNotifyXmlResult = new StringBuilder(); //逐行解析
String readline = null;
while ((readline = brf.readLine()) != null){
wxpayNotifyXmlResult.append(readline);
} log.info("------ 3.微信支付结果异步回调内容:{} ------",wxpayNotifyXmlResult); //调用业务接口,解析异步回调的支付结果,并获取通知微信官方的结果
String respWechatXmlResult = wechatPayService.getNotifyWechatXmlResult(wxpayNotifyXmlResult.toString()); //同步给微信官方响应结果
PrintWriter out = response.getWriter();
out.write(respWechatXmlResult);
out.flush();
out.close(); log.info("------ 4、微信支付异步回调处理后,响应结果:{} ------",respWechatXmlResult); }catch (Exception e){ log.info("------ 微信支付结果异步回调处理异常,异常信息:{} ------", e.getMessage()); } }

4.4 请求测试(微信官方回调)

5、查看用户订单状态

  1. 获取参数,放进map集合中并按key值,字典排序。
  2. 通过参数生成签名(生成的签名也放进map集合)。
  3. 将map集合转成xml字符串。
  4. 获取订单状态查询地址,xml参数字符串作为参数发送请求。
  5. 返回订单状态信息。

5.0 参数列表

5.1 接口

/**
* @author : huayu
* @date : 3/11/2022
* @param : [orderNo]
* @return : java.lang.String
* @description : 通过订单好查询订单状态
*/
Map<String, String> getOrderStatus(String orderNo) throws Exception;

5.2 实现类

@Override
public Map<String, String> getOrderStatus(String orderNo) throws Exception { log.info("++++++ 获取查询订单状态的参数 ++++++"); //参数集合
TreeMap<String, String> paramsMap = new TreeMap<>(); //公众账号ID appid
paramsMap.put("appid",wechatPayConfig.getAppId()); //商户号 mch_id
paramsMap.put("mch_id",wechatPayConfig.getMchId()); //商户订单号 out_trade_no
paramsMap.put("out_trade_no",orderNo); //随机字符串 nonce_str
paramsMap.put("nonce_str",WechatPayUtil.generateNonceStr()); //签名 sign
paramsMap.put("sign",WechatPayUtil.generateSignature(paramsMap,wechatPayConfig.getMchKey())); //将map集合的参数转换成xml格式的字符串
String paramsMapToXml = WechatPayUtil.generateMapToXml(paramsMap); log.info("++++++ 查询订单状态的参数:{} ++++++",paramsMapToXml); //发送post请求,获取订单状态 xml格式字符串
String orderStatusXmlResult = HttpClient4Util.getResponse4PostByString(wechatPayConfig.getViewOrderStatusUri()
, paramsMapToXml
, WechatPayConstant.WECHAT_PAY_ENCODING_UTF8); log.info("++++++发送post请求,获取订单状态 xml格式字符串 orderStatusXmlResult: ++++++\n{}",orderStatusXmlResult); //将xml格式结果字符串转化成 map 集合
Map<String, String> orderStatusXmlResultToMap = WechatPayUtil.generateXmlToMap(orderStatusXmlResult); //判断返回结果
//交易成功判断条件: return_code、result_code和trade_state都为SUCCESS
if(WechatPayConstant.WECHAT_PAY_VIEW_ORDER_STATUS_SUCCESS.equals(orderStatusXmlResultToMap.get("return_code"))
&& WechatPayConstant.WECHAT_PAY_VIEW_ORDER_STATUS_SUCCESS.equals(orderStatusXmlResultToMap.get("result_code"))
&& WechatPayConstant.WECHAT_PAY_VIEW_ORDER_STATUS_SUCCESS.equals(orderStatusXmlResultToMap.get("trade_state"))
){ //返回订单信息
return orderStatusXmlResultToMap; } return null;
}

5.3 请求方法

/**
* @author : huayu
* @date : 3/11/2022
* @param : [orderNo]
* @return : void
* @description : 查看用户订单状态
*/
@RequestMapping("/wxpay/viewOrderStatus")
public RequestResult<String> viewOrderStatus(@RequestParam String orderNo) throws Exception { //调用业务层 查询订单状态
String orderStatus = wechatPayService.getOrderStatus(orderNo); if(orderStatusInfoMap != null){
return ResultBuildUtil.success(orderStatusInfoMap);
} return ResultBuildUtil.fail(null); }

5.4 请求测试

5.4.1 发起请求

5.4.2 请求结果

SpringCloud(七) - 微信支付的更多相关文章

  1. 在Web应用中接入微信支付的流程之极简清晰版

    在Web应用中接入微信支付的流程之极简清晰版 背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可. 没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布 ...

  2. 在Web应用中接入微信支付的流程之极简清晰版 (转)

    在Web应用中接入微信支付的流程之极简清晰版 背景: 在Web应用中接入微信支付,我以为只是调用几个API稍作调试即可. 没想到微信的API和官方文档里隐坑无数,致我抱着怀疑人生的心情悲愤踩遍了丫们布 ...

  3. Android开发 --微信支付开发(转载!)(开发工具:Eclipse)

    Android_APP 微信支付接口开发 日期:2015-10-06 12:47:33 作者: 来源: 人气:3549 1.首先说一下我们在开发微信支付接口的时候遇到最多和最疑惑的问题,那就是明明 a ...

  4. 浅析微信支付:公众平台卡券功能开通、HTML5线上发券(JS-SDK接口)、查看卡券详情

    本文是[浅析微信支付]系列文章的第十六篇,主要讲解如何使用微信公众平台的卡券功能.如何使用HTML5在网页展示用户领券以及微信卡券和商户平台代金券的关系. 浅析微信支付系列已经更新十六篇了哟-,没有看 ...

  5. 微信支付-微信公众号支付,微信H5支付,微信APP支付,微信扫码支付

    在支付前,如果使用第三方MVC框架,则使用重写模式,服务器也需要配置该项 if (!-e $request_filename){ rewrite ^/(.*)$ /index.php/$ last; ...

  6. 微信支付开发出现redirect_uri参数错误的解决方法

    我们在进行微信支付开发的时候会遇到出现“redirect_uri参数错误”这种情况,怎么办呢?下面就是我总结出现这种“redirect_uri参数错误”的七种可能情况,以及解决方式. 1.可能原因①: ...

  7. Java 微信支付分对接记录 (先享后付)

    微信支付分(先享后付)对接记录: 微信支付分对接步骤 填写开通支付分的申请表格 此步骤大概需要审核 1-3 个工作日; (模板-服务信息配置表-[先享后付免确认]-[商户名].xls) 填写商户信息 ...

  8. 【zhifu】web开发中的支付宝支付和微信支付

    一.支付类型: 支付宝支付: 支付宝app内的网页支付: app外(即普通浏览器)网页支付: 微信支付: 微信app内的支付(在这里叫公众号支付) app外的支付(微信H5支付): 微信公众号的支付宝 ...

  9. 微信支付万亿日志在Hermes中的实践

    导语 | 微信支付日志系统利用 Hermes 来实现日志的全文检索功能,自从接入以来,日志量持续增长.目前单日入库日志量已经突破万亿级,单集群日入库规模也已经突破了万亿,存储规模达 PB 级.本文将介 ...

随机推荐

  1. 【lwip】005-lwip内核框架剖析

    目录 前言 5.1 lwip初始化 5.2 内核超时 5.2.1 内核超时机制 5.2.2 周期定时机制 5.2.3 内核超时链表数据结构 5.2.4 内核超时初始化 5.2.6 超时的溢出处理 5. ...

  2. SpringBoot 整合 MongoDB 实战介绍

    一.介绍 在前面的文章中,我们详细的介绍了 MongoDB 的配置和使用,如果你对 MongoDB 还不是很了解,也没关系,在 MongoDB 中有三个比较重要的名词:数据库.集合.文档! 数据库(D ...

  3. 字节微服务HTTP框架Hertz使用与源码分析|拥抱开源

    一.前言 Hertz[həːts] 是一个 Golang 微服务 HTTP 框架,在设计之初参考了其他开源框架 fasthttp.gin.echo 的优势, 并结合字节跳动内部的需求,使其具有高易用性 ...

  4. 理解 KingbaseES 中的递归查询

    关键字:SQL,CTE,递归查询 概述:通常递归查询是一个有难度的话题,尽管如此,它们仍使您能够完成在 SQL 中无法实现的操作.本文通过示例进行了简单介绍,并展示了与 PL/SQL的递归查询实现的差 ...

  5. EntityFrameworkCore 模型自动更新(上)

    话题 嗯,距离上一次写博文已经过去近整整十个月,还是有一些思考,但还是变得懒惰了,心思也不再那么专注,有点耗费时间,学习也有点停滞不前,那就顺其自然,随心所欲吧,等哪天心血来潮,想写了就写写 模型自动 ...

  6. C#,根据路径获取某个数字开头的所有文件夹,并获取最新文件夹进行替换文件

    项目需求获取某路径下为1开头文件夹,并替换最新文件夹内容,话不多说,上代码 private void Form1_Load(object sender, EventArgs e) { try { st ...

  7. Python数据科学手册-机器学习: 决策树与随机森林

    无参数 算法 随机森林 随机森林是一种集成方法,集成多个比较简单的评估器形成累计效果. 导入标准程序库 随机森林的诱因: 决策树 随机森林是建立在决策树 基础上 的集成学习器 建一颗决策树 二叉决策树 ...

  8. SQL 时间范围和时间粒度

    前言 使用 SQL 进行业务数据计算时,经常会遇到两个概念:时间范围 和 时间粒度 .以 最近一天的每小时的用户访问人数 为例: 最近一天 是时间范围 每小时 是时间粒度 常见的时间范围:最近五分钟. ...

  9. Kubernetes 监控:Prometheus Adpater =》自定义指标扩缩容

    使用 Kubernetes 进行容器编排的主要优点之一是,它可以非常轻松地对我们的应用程序进行水平扩展.Pod 水平自动缩放(HPA)可以根据 CPU 和内存使用量来扩展应用,前面讲解的 HPA 章节 ...

  10. kube-scheduler 调度调优

    文章转载自:https://www.kuboard.cn/learning/k8s-advanced/schedule/tuning.html kube-scheduler 是 Kubernetes ...