SpringBoot 搭建微信小程序支付(JSAPI) 纯后端
一、支付流程
PS:做这个之前 ,先去下载官方的SDK吧 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=11_1
1.首先要拿到appid,key,AppSecret, mch_id (商户号) 。 这几个参数由你或者是你公司去申请
2.前端需要给后端的(统一下单接口)传递code(登录凭证),标题body(自定义,比如游戏充值)和支付金额 。
3.后端需要有统一下单的接口和一个支付成功后的回调接口
4.统一下单接口在收到这三个参数后,先根据code去获取openid(JSAPI必传openid),
官网放出的获取openid的请求地址 需要appid ,secret, code, grant_type的值写成固定的->authorization_code
GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
5.然后把各种参数封装为一个map 再根据secret和map进行MD5加密返回一个签名 ,再把这个签名封装到map中
最后 发送请求并返回结果
6.获取到第一次签名请求的结果后,再对返回的数据进行第二次签名,最后返回给前端一系列的参数
7.前端在收到返回的参数后,如果为success ,就调起微信支付页面,付款成功后微信会自动调用我们定义的回调函数,在回调函数中需要响应结果给微信系统,表示我们已经支付完成 ,不然微信会调用我们的回调函数8次。
8.回调函数会返回结果,如果为success就表示此次支付成功完成
官网放出的获取openid的请求地址 需要appid , secret,code,grant_type写成固定的->authorization_code
具体代码:
统一下单,回调函数,查询订单--具体代码:
注意: 这里面的地址我都不是用的localhost ,需要弄成外网的样子 所以这里我做了一个内网穿透 不然回调函数无法执行
内网穿透我用的是ngrok,官网下载后启动执行ngrok http 端口号 。然后就会生成一行地址,你把你接口的地址加在生成的地址后面就可以访问了
1.
package com.github.wxpay.sdk.wxpaydemo.controller;import com.github.wxpay.sdk.wxpaydemo.config.WXPay;
import com.github.wxpay.sdk.wxpaydemo.config.WXPayConstants;
import com.github.wxpay.sdk.wxpaydemo.config.WXPayUtil;
import com.github.wxpay.sdk.wxpaydemo.domain.OrderQueryReturnDomain;
import com.github.wxpay.sdk.wxpaydemo.domain.OrderReturnDomain;
import com.github.wxpay.sdk.wxpaydemo.domain.PayDomain;
import com.github.wxpay.sdk.wxpaydemo.util.PayUtil;
import com.github.wxpay.sdk.wxpaydemo.util.RandomStringGenerator;
import com.github.wxpay.sdk.wxpaydemo.util.WxPayGobal;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.XStream;
import org.springframework.web.bind.annotation.*;import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;/*** 微信小程序支付接口*/@RestController
@RequestMapping("/pay")
public class wxpay {//JSAPI和APP 统一下单请求地址 (固定的)private static final String JSAPI_URL="https://api.mch.weixin.qq.com/pay/unifiedorder";//支付回调地址(你自己的请求地址,可以自己随意配置啦,写在这方便你理解)private static final String NOTIFY_URL = "http://04a3af668030.ngrok.io/pay/back/wxNotify";//支付订单查询地址 (固定的)private static final String ORDER_URL="https://api.mch.weixin.qq.com/pay/orderquery";//交易类型(这里是公众号支付)private static final String TRADE_TYPE = "JSAPI";/*** JSAPI 统一下单接口** @param code 登录凭证* @param body 标题* @param money 支付金额* @return 返回交易类型和预支付会话标识* @throws Exception*/@PostMapping("/wxJSApiPay")public Map<String, Object> wxJSApiPay(String code, String body, String money) throws Exception {String orderNumber = WxPayGobal.orderNumber();Map<String, String> data = new HashMap<String, String>();Map<String, Object> map = new HashMap<String, Object>();data.put("appid", PayDomain.getAppid()); //appiddata.put("mch_id", PayDomain.getMch_id()); //商户iddata.put("body", body); //标题data.put("out_trade_no", orderNumber);//订单号data.put("total_fee", money); //金额 单位为分data.put("spbill_create_ip", InetAddress.getLocalHost().getHostAddress());//终端IPdata.put("notify_url", NOTIFY_URL); //回调地址data.put("trade_type", TRADE_TYPE); // 交易类型data.put("openid", WxPayGobal.getOpenId(code)); //用户标识data.put("nonce_str", RandomStringGenerator.getRandomStringByLength(32)); //随机字符串System.out.println(data);String result = WxPayGobal.wxPay(data,JSAPI_URL); //传递参数发送请求并返回结果XStream xStream = new XStream();//出于安全考虑,这里必须限制类型,不然会报错xStream.allowTypes(new Class[]{OrderReturnDomain.class});xStream.alias("xml", OrderReturnDomain.class);OrderReturnDomain s = (OrderReturnDomain) xStream.fromXML(result); //返回结果转换为实体//当请求返回结果为success时 进行二次签名if ("SUCCESS".equals(s.getReturn_code()) && s.getReturn_code().equals(s.getResult_code())) {//生成签名(官方给出来的签名方法)Map<String, String> map2 = new HashMap<String, String>();long time = System.currentTimeMillis() / 1000;map2.put("appId", PayDomain.getAppid());map2.put("timeStamp", String.valueOf(time));//这边的随机字符串必须是第一次生成sign时,微信返回的随机字符串,不然小程序支付时会报签名错误map2.put("nonceStr", s.getNonce_str());map2.put("package", "prepay_id=" + s.getPrepay_id());map2.put("signType", "MD5");String sign2 = WXPayUtil.generateSignature(map2, PayDomain.getKey(), WXPayConstants.SignType.MD5);System.out.println("二次签名的sign2----->" + sign2);Map<String, Object> payInfo = new HashMap<String, Object>();payInfo.put("timeStamp", String.valueOf(time));payInfo.put("nonceStr", s.getNonce_str());payInfo.put("package", "prepay_id="+s.getPrepay_id());payInfo.put("signType", "MD5");payInfo.put("paySign", sign2);payInfo.put("orderNumber",orderNumber);map.put("status", 200);map.put("msg", "success");map.put("data", payInfo);//此处写逻辑代码实现/*** --* --*/} else {return null;}return map;}/**** @param request* @param response* @param orderNumber 下单成功后的订单号* @param sign 下单成功后的签名* @return* @throws Exception*/@PostMapping(value = "/orderQuery")public static OrderQueryReturnDomain orderQuery(HttpServletRequest request, HttpServletResponse response,String orderNumber,String sign) throws Exception {Map<String, String> data = new HashMap<String, String>();data.put("appid", PayDomain.getAppid());data.put("mch_id", PayDomain.getMch_id());data.put("out_trade_no", orderNumber);//订单号data.put("nonce_str", RandomStringGenerator.getRandomStringByLength(32)); //随机字符串data.put("sign",sign); //签名System.out.println(data);String s = WxPayGobal.wxPay(data,ORDER_URL); //传递参数发送请求并返回结果XStream xStream = new XStream();OrderQueryReturnDomain result = (OrderQueryReturnDomain) xStream.fromXML(s);return result;}/*** 微信小程序支付成功回调函数** @param request* @param response* @throws Exception*/@RequestMapping(value = "/callback/wxNotify")public static void wxNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream) request.getInputStream()));String line = null;StringBuilder sb = new StringBuilder();while ((line = br.readLine()) != null) {sb.append(line);}br.close();//sb为微信返回的xmlString notityXml = sb.toString();String resXml = "";System.out.println("接收到的报文:" + notityXml);@SuppressWarnings("unchecked")Map<String, String> map = PayUtil.doXMLParse(notityXml);String returnCode = (String) map.get("return_code");if ("SUCCESS".equals(returnCode)) {//验证签名是否正确Map<String, String> validParams = PayUtil.paraFilter(map); //回调验签时需要去除sign和空值参数String validStr = PayUtil.createLinkString(validParams);//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串String sign = PayUtil.sign(validStr, PayDomain.getKey(), "utf-8").toUpperCase();//拼装生成服务器端验证的签名// 因为微信回调会有八次之多,所以当第一次回调成功了,那么我们就不再执行逻辑了//根据微信官网的介绍,此处不仅对回调的参数进行验签,还需要对返回的金额与系统订单的金额进行比对等if (sign.equals(map.get("sign"))) {/**此处添加自己的业务逻辑代码start**/// bla bla bla..../**此处添加自己的业务逻辑代码end**///通知微信服务器已经支付成功resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"+ "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";} else {System.out.println("微信支付回调失败!签名不一致");}} else {resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"+ "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";}System.out.println(resXml);System.out.println("微信支付回调数据结束");BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());out.write(resXml.getBytes());out.flush();out.close();}}
2.工具类 用于发送请求 获取openid 生成订单号(具体的订单号生成规则需要修改,我这里只是演示,不能用于上线)
package com.github.wxpay.sdk.wxpaydemo.util;import com.github.wxpay.sdk.wxpaydemo.config.WXPayUtil;
import com.github.wxpay.sdk.wxpaydemo.domain.PayDomain;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import java.io.IOException;import java.time.LocalDateTime;
import java.util.Map;import com.alibaba.fastjson.JSONObject;/*** 小程序支付工具类*/@Component
public class WxPayGobal {/*** 请求地址 (用于获取openid)*/private final static String url = "https://api.weixin.qq.com/sns/jscode2session";private static Logger logger = LoggerFactory.getLogger(WxPayGobal.class);public static String wxPay(Map<String, String> data,String url) {//生成签名String sign = null;try {sign = WXPayUtil.generateSignature(data, PayDomain.getKey());} catch (Exception e) {e.printStackTrace();}data.put("sign", sign);String s = null;try {s = WXPayUtil.mapToXml(data);} catch (Exception e) {e.printStackTrace();}//发生请求并返回请求结果String s1 = doPostXml(url, s);System.out.println(s1);return s1;}/*** 发送请求** @param url 请求地址* @param requestDataXml 请求的xml类型的字符串* @return*/public static String doPostXml(String url, String requestDataXml) {CloseableHttpClient httpClient = null;CloseableHttpResponse httpResponse = null;httpClient = HttpClients.createDefault();HttpPost httpPost = new HttpPost(url);RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(15000).setConnectionRequestTimeout(60000).setSocketTimeout(60000).build();httpPost.setConfig(requestConfig);httpPost.setEntity(new StringEntity(requestDataXml, "UTF-8"));httpPost.addHeader("Content-Type", "text/xml");String result = "";try {httpResponse = httpClient.execute(httpPost);HttpEntity entity = httpResponse.getEntity();result = EntityUtils.toString(entity, "UTF-8");} catch (Exception e) {e.printStackTrace();}return result;}/*** 获取openId* @param code 登录凭证* @return*/public static String getOpenId(String code) {//请求参数String params = url + "?appid=" + PayDomain.getAppid() + "&secret=" + PayDomain.getSecret() + "&js_code=" + code + "&grant_type=" + "authorization_code";logger.info("请求地址:"+params);//发送请求HttpGet httpGet = new HttpGet(params);CloseableHttpClient httpClients = HttpClients.createDefault();String result = null;try {CloseableHttpResponse execute = httpClients.execute(httpGet);HttpEntity entity = execute.getEntity();result = EntityUtils.toString(entity);System.out.println(result+"...");} catch (IOException e) {e.printStackTrace();}JSONObject jsonObject = JSONObject.parseObject(result);logger.info("获取openid" + jsonObject.getString("openid"));if (null!=jsonObject.getString("openid")) {String openid = jsonObject.getString("openid");return openid;}return null;}/*** 订单号生成 当前日期+随机数字+商户ID*/public static String orderNumber(){LocalDateTime now = LocalDateTime.now();String str=now.getYear()+""+now.getMonthValue()+now.getDayOfMonth()+"";//当前年月日String randomNumber=""; //随机数字for (int i = 0; i < 6; i++) {int max=9,min=0;int ran2 = (int) (Math.random()*(max-min)+min);randomNumber=ran2+randomNumber;}System.out.println(str+randomNumber);return str+randomNumber;}}
实体类:
package com.github.wxpay.sdk.wxpaydemo.domain;import lombok.Data;/*** 接收统一下单调用微信接口返回的参数* @author Administrator* */
@Data
public class OrderReturnDomain {/*** 返回状态码 (SUCCESS或 fail)*/private String return_code;/*** 返回信息*/private String return_msg;/*** 返回的结果状态码*/private String result_code;/*** 返回的appid*/private String appid;/*** 返回的商户号*/private String mch_id;/*** 返回的随机字符串*/private String nonce_str;/*** 返回的签名*/private String sign;/***微信生成的预支付会话标识,用于后续接口调用中使用,该值有效期为2小时*/private String prepay_id;/*** 返回的交易类型 (JSAPI,APP,NATIVE)*/private String trade_type;/*** 返回的错误码*/private String err_code;/*** 返回的错误信息描述*/private String err_code_des;
}
package com.github.wxpay.sdk.wxpaydemo.domain;import lombok.Data;/*** 封装订单支付成功后的返回结果*/@Data
public class OrderQueryReturnDomain {private String return_code; //返回返回状态码private String return_msg; //返回信息private String appid; //公众账号IDprivate String mch_id; //商户号private String nonce_str; //随机字符串private String sign; //签名private String result_code; //业务结果private String openid; //用户标识private String is_subscribe; //是否关注公众账号private String trade_type; //交易类型private String bank_type; //付款银行private String total_fee; //标价金额private String fee_type; //标价币种private String transaction_id; //微信支付订单号private String out_trade_no; //商户订单号private String time_end; //支付完成时间private String trade_state; //交易状态private String cash_fee; //现金支付金额private String trade_state_desc;//交易状态描述private String cash_fee_type; //现金支付币种}
package com.github.wxpay.sdk.wxpaydemo.domain;import com.github.wxpay.sdk.wxpaydemo.config.WXPayConfig;
import lombok.Data;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;@Component
public class PayDomain {/*** 密钥key*/private static String key;/*** 应用ID*/private static String appid;/*** 商户号*/private static String mch_id;/*** 密钥* @param key*/private static String secret;@Value("${com.wx.wxpay.key}")public void setKey(String key) {this.key = key;}@Value("${com.wx.wxpay.appid}")public void setAppid(String appid) {this.appid = appid;}@Value("${com.wx.wxpay.mch_id}")public void setMch_id(String mch_id) {this.mch_id = mch_id;}@Value("${com.wx.wxpay.secret}")public void setSecret(String secret) {PayDomain.secret = secret;}public static String getSecret() {return secret;}public static String getKey() {return key;}public static String getAppid() {return appid;}public static String getMch_id() {return mch_id;}}
pom:
<dependencies>
<!-- map和xml之间的转换器--><dependency><groupId>com.thoughtworks.xstream</groupId><artifactId>xstream</artifactId><version>1.4.9</version></dependency>
<!-- 操作xml标签--><dependency><groupId>org.jdom</groupId><artifactId>jdom</artifactId><version>2.0.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.4</version></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.3</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.18</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies>
记得一定要下载官方SDK !!! 然后把我这些代码加上去就OK 。
由于需要得朋友太多了,我就直接上传文件吧。
完整demo文件下载地址:不需要积分
https://download.csdn.net/download/aSmart_Q/36142998
觉得好用,给我点个赞吧!!感谢
SpringBoot 搭建微信小程序支付(JSAPI) 纯后端相关推荐
- SpringBoot对接微信小程序支付功能开发(一,下单功能)
1,接入前准备: 接入模式选择直连模式: 申请小程序,得到APPID,并开通微信支付: 申请微信商户号,得到mchid,并绑定APPID: 配置商户API key,下载并配置商户证书,根据微信官方文档 ...
- SpringBoot对接微信小程序支付功能开发(二,支付回调功能)
接着上一篇: SpringBoot对接微信小程序支付功能开发(一,下单功能) 在上一篇下单功能中我们有传支付结果回调地址. 下面是回调接口实现 package com.office.miniapp.c ...
- 关于使用nodejs搭建微信小程序支付接口
前言 前段时间在开发一个微信小程序的时候需要用到支付功能,我就去看了下微信支付的官方文档,好家伙,微信官方只提供了java.php还有Go语言的sdk.PHP我会点吧,但又不是很会,做为一个菜鸡前端, ...
- SpringBoot实现微信小程序支付
本文给大家讲解微信小程序支付全流程,以及相关功能源代码,项目不开放,带来不便尽请谅解.小程序支付主要 包含如下几步骤,1.预下单-调用微信统一下单接口进行预下单.2.小程序拿到支付参数唤醒支付.3.支 ...
- 微信小程序支付(java后端)
微信支付文档传送门:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3 1.开发工具: idea+springclou ...
- 微信小程序支付以及退款功能(超详细)
Springboot整合微信小程序支付 第一步: 第二步:导入微信支付的依赖 第三步:在springboot核心配置文件中配置参数 第四步:创建配置类WxPayAppConfig 第五步:支付方法 s ...
- 微信小程序支付流程(Java后端)
微信小程序支付(Java后端) 一.小程序支付的交互图如下 按住ctrl点击 微信支付平台开发文档 二.准备工作 第一步:在pom文件中导入微信支付SDK 有可能自动下载不了,可以到微信支付平台下载手 ...
- 基于springboot微信小程序支付功能实现
基于springboot微信小程序支付功能实现 简单的封装微信小程序支付功能,支付工具类所依赖的fastjson.lombok.wagegger, 1.添加maven依赖: 版本号可根据自己项目的实际 ...
- 微信小程序调用JSAPI进行微信支付
微信小程序调用JSAPI进行微信支付 1.小程序中先调用接口在微信支付服务后台生成预支付交易单,返回正确的预支付交易后调起支付: 2.商户server调用再次签名: 3.唤起微信支付页面,进行支付: ...
最新文章
- 如何看待亚马逊 AI 李沐团队大批人员离职?
- 2019年25大人工智能趋势!一文看到未来
- 解决 'config.h' file not found。升级xcode 新版本导致旧的RN项目打开报错 解决方法。...
- 前端开发面试题总结之——CSS3
- 博为峰Java技术文章 ——JavaSE Swing JPanel III
- 详解RPC远程调用和消息队列MQ的区别
- 本地上传文件到Linux云服务器
- android使用greedao踩坑日记
- Ubuntu使用谷歌浏览器登录账号时崩溃
- 黑马程序员全套Java教程_Java基础入门视频教程零基础自学Java必备教程视频讲义(3)
- Java网络编程-简易聊天室源码分享
- CRCNN PCNN
- 汽车制造商代码表_汽车制造商合作打造未来汽车
- 中国菜刀使用与原理分析
- 大型网站图片服务器架构的演进!
- Flask入门flask-script 蓝本 钩子函数(3)
- 5g cpe参数及功能介绍
- TIA博途中使用T-CONFIG指令修改S7-1200 PLC的IP地址和设备名称
- php 变量 问号,php – Laravel查询返回问号而不是变量值
- php开发商城系统怎么样?有哪些PHP开源商城系统推荐?
热门文章
- 微软魔镜——从“解语花”到“读心树”的神奇魔法
- 在金融行业中,直播获客应该怎么做呢?
- SUSE Linux Enterprise助力企业数字化转型
- 在键盘上同时按3个键有时会有一个键不起作用是什么原因?
- 《数字图像处理》题库5:计算题 ②
- matlab与abaqus结构优化,基于MATLAB和ABAQUS的螺旋钻杆参数优化与数值模拟
- 标题Macvim连接Mac终端前期终端配置
- 未来的计算机科技画,未来科技生活绘画作品有哪些
- Unity_Skybox自定义插件可实现日夜更替Polyverse Skies | Low Poly
- CTFshow sql注入 上篇(web221-253)