文章目录
前言
本文适用于有一定基础的开发者,简单易通。后台用的的是java,我用的是springBoot,其它框架基本同理,前端就是一个简单的demo。微信官方提供了V2和V3两种方式,本文基于V2版支付开发(后续更新V3)。V2和V3版本区别
微信支付接入前准备
1.思路介绍
本次以微信小程序开发为例,如果自己想要玩一下,需要在微信公众平台注册一个小程序账号,其次微信支付
平台,进行接入微信支付,然后就可以着手开发了。(公司开发的话,参数账号之类都会提供的,不用担心)
2.如何注册小程序账号?
可以直接进入微信公众平台直接注册,也可根据文档指引进行注册。申请账号官方详细文档
如商户已拥有自己的APP,且希望该APP接入微信支付,请前往 开放平台申请
3.如何接入微信支付,需要准备哪些东西?
简单介绍一下自己的经验,重点是营业执照信息。关键这玩意怎么搞方面快捷呢。
我提供两种方案:
1.最方便的是自己注册个电商营业执照,拼多多商铺0元直接入驻即可。注册地址要和营业执照地址相同(要有房产证明等,写自己老家就行)。
2.如果自己租的房子,合同中必须体现可以作为商铺,要不然没法。 直接注册实体个体工商户,目前非常方便线下线上都可以办理-不同地方的软件不同 这里以杭州为例,直接下载浙里办-搜索个体工商户,在线办理即可。
最重要的是地址(可以借助朋友,或者租房的时候说好可以作为商铺,当然自己本地有房子咋样搞都行),其次网上那些挂靠地址等,如果你可以承担费用,自主决定。
请参考微信支付商户号申请,详细的一批。
4.小程序接入前的准备(APPID及mchid以上两步已拿到,只需配置一下API KEY即可)
官方文档指引-小程序支付
注意:API秘钥,一定要记住。最好记事本记录一下。
由于本文基于V2版支付开发,记得设置一下。
微信支付-完整代码
前端代码
index.wxml
<view class="container">
<view class="container">
<input type="text" bindinput="getOrderCode" style="border:1px solid #ccc;" />
<button type="primary" bindtap="pay">微信支付</button>
</view>
</view>
index.js
const app = getApp()
Page({
data: {
txtOrderCode: '',
},
pay: function () {
var ordercode = this.data.txtOrderCode;
wx.login({
success: function (res) {
console.log("code值是多少"+res.code)
if (res.code) {
wx.request({
url: 'http://localhost:8081/wechatPay/wxPay',
data: {
code: res.code,//要去换取openid的登录凭证
money: ordercode,//支付金额(主要是code和money其他都是业务参数可去掉)
name:"",
idCard:"",
post:"",
phone:"",
receiverInfo:"",
duesDate:"",
partyOrg:"",
agyCode:"",
agyName:"",
orderId:""
},
method: 'POST',
success: function (res) {
console.log("是否进来方法"+res.data.package)
//通过接口返回的data,调用此方法唤起支付页面
wx.requestPayment({
//这里的参数和值基本都是固定,不用更改
timeStamp: res.data.timeStamp,
nonceStr: res.data.nonceStr,
package: res.data.package,
signType: 'MD5',
paySign: res.data.paySign,
success: function (res) {
console.log("支付成功")
console.log(res);
},
fail: function (res) {
console.log("支付失败");
wx.showToast({
title: '支付失败',
icon:'none',
duration:2000
})
},
complete: function (res) {
console.log('支付完成');
if (res.errMsg == 'requestPayment:ok') {
wx.showModal({
title: '提示',
content: '支付成功'
});
}
}
})
},
fail: function (res) {
wx.showToast({
title: '调用接口失败',
icon:'none',
duration:2000
})
},
})
} else {
console.log('获取用户登录态失败!' + res.errMsg)
}
}
});
},
getOrderCode: function (event) {
console.log(event)
this.setData({
txtOrderCode: event.detail.value
});
},
onLoad: function () {
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
} else if (this.data.canIUse){
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
app.userInfoReadyCallback = res => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
} else {
// 在没有 open-type=getUserInfo 版本的兼容处理
wx.getUserInfo({
success: res => {
app.globalData.userInfo = res.userInfo
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
})
}
},
})
效果图
后端代码
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hn.yuan</groupId>
<artifactId>demo_wxpay</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo_wxpay</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--解析xml-->
<dependency>
<groupId>org.jdom</groupId>
<artifactId>jdom2</artifactId>
<version>2.0.6</version>
</dependency>
<!--Java对象和JSON数据之间进行映射的Java类库-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<!--处理输入输出流-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk16</artifactId>
<version>1.46</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.21</version>
</dependency>
<!--小程序支付包-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-apache-httpclient</artifactId>
<version>0.4.8</version>
</dependency>
<!--http commons client-->
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.1</version>
</dependency>
<!--springboot整合redisCluster-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!--redis框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.yml
server:
port: 8081
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/yuan_productlist?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: root
# 配置slq打印日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
log.path: F:/logs #打印日志路径
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--注意resource属于.yml还是.properties-->
<property resource="application.yml"/>
<!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径 使用spring-boot的配置项LOG_PATH-->
<springProperty scope="context" name="LOG_HOME" source="log.path"/>
<!--下面用于application.properties-->
<!-- <property name="LOG_HOME" value="${log.path}" />-->
<property name="LOG_LEVEL" value="INFO" />
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--日志文件输出的文件名-->
<FileNamePattern>${LOG_HOME}/springbootjpa.log.%d{yyyy-MM-dd}.log</FileNamePattern>
<!--日志文件保留天数-->
<MaxHistory>30</MaxHistory>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
<!--日志文件最大的大小-->
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>10MB</MaxFileSize>
</triggeringPolicy>
</appender>
<logger name="com.wm.springbootjpa01" level="INFO"/>
<logger name="org.apache.ibatis" level="${LOG_LEVEL}"/>
<logger name="org.mybatis.spring" level="${LOG_LEVEL}"/>
<logger name="org.springframework" level="${LOG_LEVEL}"/>
<logger name="java.sql.Connection" level="${LOG_LEVEL}"/>
<logger name="java.sql.Statement" level="${LOG_LEVEL}"/>
<logger name="java.sql.PreparedStatement" level="${LOG_LEVEL}"/>
<!--设置为OFF,即屏蔽; 留下sqltiming作为INFO级别输出-->
<logger name="jdbc.connection" level="OFF"/>
<logger name="jdbc.resultset" level="OFF"/>
<logger name="jdbc.resultsettable" level="OFF"/>
<logger name="jdbc.audit" level="OFF"/>
<logger name="jdbc.sqltiming" level="INFO"/>
<logger name="jdbc.sqlonly" level="OFF"/>
<!-- 日志输出级别 -->
<root level="${LOG_LEVEL}">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>
WechatConfig支付配置类
package com.hn.yuan.wxinterface;
/**
* 支付配置类
* @author XIAOCAO
**/
public class WechatConfig {
//小程序appid(开发者在微信公众平台查询)
public static final String appid ="";
//小程序appkey(开发者在微信公众平台查询)
public static final String APP_SECRET="";
//微信支付的商户id(开发者在微信商户平台查询)
public static final String mch_id = "";
//微信支付的商户密钥(开发者在微信商户平台查询)
public static final String key = "";
//支付成功后的服务器回调url(后台随便的一个接口能接收到就行,有效地址)
public static final String notify_url = "";
//签名方式,固定值
public static final String SIGNTYPE = "MD5";
//交易类型,小程序支付的固定值为JSAPI
public static final String TRADETYPE = "JSAPI";
//微信统一下单接口地址
public static final String pay_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
}
WxPayDto类
package com.hn.yuan.entity;
import lombok.Data;
import java.io.Serializable;
@Data
public class WxPayDto implements Serializable {
private static final long serialVersionUID = 1L;
private String partyOrg;
private String duesDate;
private String money; //缴费金额
private String name;
private String idCard;
private String post;
private String phone;
private String orderId;
private String receiverInfo;
private String code;//获取openid临时凭证
private String agyCode;
private String agyName;
}
业务代码WxpayController
package com.hn.yuan.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.hn.yuan.entity.WxPayDto;
import com.hn.yuan.wxinterface.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import static com.hn.yuan.wxinterface.WXPayConstants.RETURN_CODE;
import static com.hn.yuan.wxinterface.WechatConfig.APP_SECRET;
@CrossOrigin
@RestController
@RequestMapping("/wechatPay")
public class WxpayController {
private static Logger logger = LoggerFactory.getLogger(WxpayController.class);
@Autowired
private HttpServletRequest request;
@Resource
private HttpServletResponse response;
@PostMapping("/wxPay")
public String wxPay(@RequestBody WxPayDto dto) {
System.out.println("进来方法了");
Object result = new Object();
try {
//获取客户端的ip地址
String spbill_create_ip = getIpAddr(request);
System.out.println("客户端Ip地址" + spbill_create_ip);
System.out.println("code是什么样式的" + dto.getCode());
//获取openid
String openid = getOpenId(dto.getCode());
//订单号 uuid 随机生成
String outTradeNo = WXPayUtil.generateUUID();
//支付业务
result = wxPay(spbill_create_ip, openid, outTradeNo, dto);
} catch (Exception e) {
e.printStackTrace();
}
return PayUtil.toJson(result);
}
//获取IP
private String getIpAddr(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
//多次反向代理后会有多个ip值,第一个ip才是真实ip
int index = ip.indexOf(",");
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
}
ip = request.getHeader("X-Real-IP");
if (StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {
return ip;
}
return request.getRemoteAddr();
}
/**
* 缴纳费用
**/
public Map wxPay(String spbill_create_ip, String openId, String orderNumber, WxPayDto dto) {
Map<String, String> payMap = new HashMap<String, String>();//返回给小程序端需要的参数
try {
logger.info("【小程序支付】 统一下单开始, 订单编号=" + orderNumber);
//商品名称
String body = dto.getAgyName() + "-" + dto.getName() + "-测试费用";
//获取客户端的ip地址
BigDecimal money = new BigDecimal(dto.getMoney());
//组装参数,用户生成统一下单接口的签名
logger.info("----------下单接口签名-------");
Map<String, String> packageParams = new HashMap<>();
//微信分配的小程序ID
packageParams.put("appid", WechatConfig.appid);
//微信支付分配的商户号
packageParams.put("mch_id", WechatConfig.mch_id);
//随机字符串
packageParams.put("nonce_str", System.currentTimeMillis() / 1000 + "");
//签名类型
packageParams.put("sign_type", "MD5");
//充值订单 商品描述
packageParams.put("body", body);
//商户订单号
packageParams.put("out_trade_no", orderNumber);
//订单总金额,单位为分
packageParams.put("total_fee", money.multiply(BigDecimal.valueOf(100)).intValue() + "");
//终端IP
packageParams.put("spbill_create_ip", spbill_create_ip);
//通知回调地址
packageParams.put("notify_url", WechatConfig.notify_url);
//交易类型
packageParams.put("trade_type", WechatConfig.TRADETYPE);
//用户标识
packageParams.put("openid", openId);
//第一次签名
String sign = WXPayUtil.generateSignature(packageParams, WechatConfig.key);
System.out.println("第一签名打印的是个啥" + sign);
packageParams.put("sign", sign);
System.out.println("字符串是啥" + PaymentKit.toXml(packageParams));
//调用支付定义下单API,返回预付单信息 prepay_id
String result = HttpKit.post(WechatConfig.pay_url, PaymentKit.toXml(packageParams));
logger.info("调试模式_统一下单接口 返回XML数据:" + result);
// 将解析结果存储在HashMap中
Map<String, String> map = PaymentKit.xmlToMap(result);
String return_code = map.get("return_code");//返回状态码
String result_code = map.get("result_code");//返回状态码
if (return_code.equals("SUCCESS") || return_code.equals(result_code)) {
//返回的预付单信息
String prepay_id = map.get("prepay_id");
payMap.put("appId", WechatConfig.appid);
payMap.put("timeStamp", System.currentTimeMillis() / 1000 + "");
payMap.put("nonceStr", System.currentTimeMillis() + "");
payMap.put("package", "prepay_id=" + prepay_id);
payMap.put("signType", "MD5");
//再次签名,这个签名用于小程序端调用wx.requesetPayment方法
String paySign = WXPayUtil.generateSignature(payMap, WechatConfig.key);
System.out.println("第二签名打印的是个啥" + sign);
logger.info("=======================第二次签名:", paySign + "============ ======");
payMap.put("paySign", paySign);
payMap.put("status", "success");
//更新订单信息
} else {
logger.info("----------统一下单失败-------");
payMap.put("status", "error");
return payMap;
}
} catch (Exception e) {
e.printStackTrace();
}
return payMap;
}
/**
* 小程序获取openid
*
* @return
*/
public static String getOpenId(String code) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
RestTemplate restTemplate = new RestTemplate();
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + WechatConfig.appid + "&secret=" + APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code";
ResponseEntity<String> responseEntity =
restTemplate.getForEntity(url, String.class);
String body = responseEntity.getBody();
JSONObject object = JSON.parseObject(body);
String openId = object.getString("openid");// 获取openId
System.out.println("该用户的openid——>" + openId);
return openId;
}
/**
* 功能描述: <小程序回调>
*
* @return:
**/
@GetMapping("/wxProPayNotify")
public void wxProPayNotify() throws Exception {
logger.info("进入微信小程序支付回调");
String xmlMsg = HttpKit.readData(request);
logger.info("微信小程序通知信息" + xmlMsg);
Map<String, String> resultMap = PaymentKit.xmlToMap(xmlMsg);
if (resultMap.get(RETURN_CODE).equals("SUCCESS")) {
String orderNo = resultMap.get("out_trade_no");
logger.info("微信小程序支付成功,订单号{}", orderNo);
//通过订单号 修改数据库中的记录,业务操作
}
String result = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
try {
response.getWriter().write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
工具类
- http,https-request类
package com.hn.yuan.wxinterface;
import javax.net.ssl.*;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import java.util.Map;
/**
* http,https-request类
**/
public class HttpKit {
private static String CHARSET = "UTF-8";
private static final SSLSocketFactory sslSocketFactory = initSSLSocketFactory();
private static final TrustAnyHostnameVerifier trustAnyHostnameVerifier = new HttpKit().new TrustAnyHostnameVerifier();
private HttpKit() {
}
private static SSLSocketFactory initSSLSocketFactory() {
try {
TrustManager[] e = new TrustManager[]{new HttpKit().new TrustAnyTrustManager()};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init((KeyManager[])null, e, new SecureRandom());
return sslContext.getSocketFactory();
} catch (Exception var2) {
throw new RuntimeException(var2);
}
}
public static void setCharSet(String charSet) {
if(charSet!=null && !charSet.equals("")) {
throw new IllegalArgumentException("charSet can not be blank.");
} else {
CHARSET = charSet;
}
}
private static HttpURLConnection getHttpConnection(String url, String method, Map<String, String> headers) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException {
URL _url = new URL(url);
HttpURLConnection conn = (HttpURLConnection)_url.openConnection();
if(conn instanceof HttpsURLConnection) {
((HttpsURLConnection)conn).setSSLSocketFactory(sslSocketFactory);
((HttpsURLConnection)conn).setHostnameVerifier(trustAnyHostnameVerifier);
}
conn.setRequestMethod(method);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setConnectTimeout(19000);
conn.setReadTimeout(19000);
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("AuthUser-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36");
if(headers != null && !headers.isEmpty()) {
Iterator i$ = headers.entrySet().iterator();
while(i$.hasNext()) {
Map.Entry entry = (Map.Entry)i$.next();
conn.setRequestProperty((String)entry.getKey(), (String)entry.getValue());
}
}
return conn;
}
public static String get(String url, Map<String, String> queryParas, Map<String, String> headers) {
HttpURLConnection conn = null;
String e;
try {
conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), "GET", headers);
conn.connect();
e = readResponseString(conn);
} catch (Exception var8) {
throw new RuntimeException(var8);
} finally {
if(conn != null) {
conn.disconnect();
}
}
return e;
}
public static String get(String url, Map<String, String> queryParas) {
return get(url, queryParas, (Map)null);
}
public static String get(String url) {
return get(url, (Map)null, (Map)null);
}
public static String post(String url, Map<String, String> queryParas, String data, Map<String, String> headers) {
HttpURLConnection conn = null;
String var6;
try {
conn = getHttpConnection(buildUrlWithQueryString(url, queryParas), "POST", headers);
conn.connect();
OutputStream e = conn.getOutputStream();
e.write(data.getBytes(CHARSET));
e.flush();
e.close();
var6 = readResponseString(conn);
} catch (Exception var10) {
throw new RuntimeException(var10);
} finally {
if(conn != null) {
conn.disconnect();
}
}
return var6;
}
public static String post(String url, Map<String, String> queryParas, String data) {
return post(url, queryParas, data, (Map)null);
}
public static String post(String url, String data, Map<String, String> headers) {
return post(url, (Map)null, data, headers);
}
public static String post(String url, String data) {
return post(url, (Map)null, data, (Map)null);
}
private static String readResponseString(HttpURLConnection conn) {
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
try {
inputStream = conn.getInputStream();
BufferedReader e = new BufferedReader(new InputStreamReader(inputStream, CHARSET));
String line = null;
while((line = e.readLine()) != null) {
sb.append(line).append("\n");
}
String var5 = sb.toString();
return var5;
} catch (Exception var14) {
throw new RuntimeException(var14);
} finally {
if(inputStream != null) {
try {
inputStream.close();
} catch (IOException var13) {
}
}
}
}
private static String buildUrlWithQueryString(String url, Map<String, String> queryParas) {
if(queryParas != null && !queryParas.isEmpty()) {
StringBuilder sb = new StringBuilder(url);
boolean isFirst;
if(url.indexOf("?") == -1) {
isFirst = true;
sb.append("?");
} else {
isFirst = false;
}
String key;
String value;
for(Iterator i$ = queryParas.entrySet().iterator(); i$.hasNext(); sb.append(key).append("=").append(value)) {
Map.Entry entry = (Map.Entry)i$.next();
if(isFirst) {
isFirst = false;
} else {
sb.append("&");
}
key = (String)entry.getKey();
value = (String)entry.getValue();
if(value!=null && !value.equals("")) {
try {
value = URLEncoder.encode(value, CHARSET);
} catch (UnsupportedEncodingException var9) {
throw new RuntimeException(var9);
}
}
}
return sb.toString();
} else {
return url;
}
}
public static String readData(HttpServletRequest request) {
BufferedReader br = null;
try {
StringBuilder e = new StringBuilder();
br = request.getReader();
String line = null;
while((line = br.readLine()) != null) {
e.append(line).append("\n");
}
line = e.toString();
return line;
} catch (IOException var12) {
throw new RuntimeException(var12);
} finally {
if(br != null) {
try {
br.close();
} catch (IOException var11) {
}
}
}
}
/** @deprecated */
@Deprecated
public static String readIncommingRequestData(HttpServletRequest request) {
return readData(request);
}
private class TrustAnyTrustManager implements X509TrustManager {
private TrustAnyTrustManager() {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
}
private class TrustAnyHostnameVerifier implements HostnameVerifier {
private TrustAnyHostnameVerifier() {
}
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
}
- 微信支付api
package com.hn.yuan.wxinterface;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
/**
* 微信支付api
* @author XIAOCAO
* @version 2020/12/25
**/
public class PaymentApi {
private PaymentApi() {}
// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
private static String unifiedOrderUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";
/**
* 交易类型枚举
* WAP的文档:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=15_1
* @author L.cm
* <pre>
* email: 596392912@qq.com
* site: http://www.dreamlu.net
* date: 2015年10月27日 下午9:46:27
* </pre>
*/
public enum TradeType {
JSAPI, NATIVE, APP, WAP, MWEB
}
/**
* 统一下单
* @param params 参数map
* @return String
*/
public static String pushOrder(Map<String, String> params) {
return HttpKit.post(unifiedOrderUrl, PaymentKit.toXml(params));
}
private static Map<String, String> request(String url, Map<String, String> params, String paternerKey) {
params.put("nonce_str", System.currentTimeMillis() + "");
String sign = PaymentKit.createSign(params, paternerKey);
params.put("sign", sign);
String xmlStr = HttpKit.post(url, PaymentKit.toXml(params));
return PaymentKit.xmlToMap(xmlStr);
}
/**
* 文档说明:https://pay.weixin.qq.com/wiki/doc/api/wap.php?chapter=15_4
* <pre>
* @param appId 公众账号ID 是 String(32) wx8888888888888888 微信分配的公众账号ID
* 随机字符串 noncestr 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位。推荐随机数生成算法
* 订单详情扩展字符串 package 是 String(32) WAP 扩展字段,固定填写WAP
* @param prepayId 预支付交易会话标识 是 String(64) wx201410272009395522657a690389285100 微信统一下单接口返回的预支付回话标识,用于后续接口调用中使用,该值有效期为2小时
* 签名 sign 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名生成算法
* 时间戳 timestamp 是 String(32) 1414561699 当前的时间,其他详见时间戳规则
* @param paternerKey 签名密匙
* </pre>
* @return {String}
*/
public static String getDeepLink(String appId, String prepayId, String paternerKey) {
Map<String, String> params = new HashMap<String, String>();
params.put("appid", appId);
params.put("noncestr", System.currentTimeMillis() + "");
params.put("package", "WAP");
params.put("prepayid", prepayId);
params.put("timestamp", System.currentTimeMillis() / 1000 + "");
String sign = PaymentKit.createSign(params, paternerKey);
params.put("sign", sign);
String string1 = PaymentKit.packageSign(params, true);
String string2 = "";
try { string2 = PaymentKit.urlEncode(string1); } catch (UnsupportedEncodingException e) {}
return "weixin://wap/pay?" + string2;
}
// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_2
private static String orderQueryUrl = "https://api.mch.weixin.qq.com/pay/orderquery";
/**
* 根据商户订单号查询信息
* @param appid 公众账号ID
* @param mch_id 商户号
* @param paternerKey 商户密钥
* @param transaction_id 微信订单号
* @return 回调信息
*/
public static Map<String, String> queryByTransactionId(String appid, String mch_id, String paternerKey, String transaction_id) {
Map<String, String> params = new HashMap<String, String>();
params.put("appid", appid);
params.put("mch_id", mch_id);
params.put("transaction_id", transaction_id);
return request(orderQueryUrl, params, paternerKey);
}
/**
* 根据商户订单号查询信息
* @param appid 公众账号ID
* @param mch_id 商户号
* @param paternerKey 商户密钥
* @param out_trade_no 商户订单号
* @return 回调信息
*/
public static Map<String, String> queryByOutTradeNo(String appid, String mch_id, String paternerKey, String out_trade_no) {
Map<String, String> params = new HashMap<String, String>();
params.put("appid", appid);
params.put("mch_id", mch_id);
params.put("out_trade_no", out_trade_no);
return request(orderQueryUrl, params, paternerKey);
}
// 文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_3
private static String closeOrderUrl = "https://api.mch.weixin.qq.com/pay/closeorder";
/**
* 关闭订单
* @param appid 公众账号ID
* @param mch_id 商户号
* @param paternerKey 商户密钥
* @param out_trade_no 商户订单号
* @return 回调信息
*/
public static Map<String, String> closeOrder(String appid, String mch_id, String paternerKey, String out_trade_no) {
Map<String, String> params = new HashMap<String, String>();
params.put("appid", appid);
params.put("mch_id", mch_id);
params.put("out_trade_no", out_trade_no);
return request(closeOrderUrl, params, paternerKey);
}
// 申请退款文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4
public static String refundUrl = "https://api.mch.weixin.qq.com/secapi/pay/refund";
// /**
// * 申请退款,内部添加了随机字符串nonce_str和签名sign
// * @param params 参数map,内部添加了随机字符串nonce_str和签名sign
// * @param paternerKey 商户密钥
// * @param certPath 证书文件目录
// * @return map
// */
// public static Map<String, String> refund(Map<String, String> params, String paternerKey, String certPath) {
// params.put("nonce_str", System.currentTimeMillis() + "");
// String sign = PaymentKit.createSign(params, paternerKey);
// params.put("sign", sign);
// String partner = params.get("mch_id");
// String xmlStr = HttpKit.delegate.postSSL(refundUrl, PaymentKit.toXml(params), certPath, partner);
// return PaymentKit.xmlToMap(xmlStr);
// }
// 查询退款文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_5
private static String refundQueryUrl = "https://api.mch.weixin.qq.com/pay/refundquery";
private static Map<String, String> baseRefundQuery(Map<String, String> params, String appid, String mch_id, String paternerKey) {
params.put("appid", appid);
params.put("mch_id", mch_id);
return request(refundQueryUrl, params, paternerKey);
}
/**
* 根据微信订单号查询退款
* @param appid 公众账号ID
* @param mch_id 商户号
* @param paternerKey 商户密钥
* @param transaction_id 微信订单号
* @return map
*/
public static Map<String, String> refundQueryByTransactionId(String appid, String mch_id, String paternerKey, String transaction_id) {
Map<String, String> params = new HashMap<String, String>();
params.put("transaction_id", transaction_id);
return baseRefundQuery(params, appid, mch_id, paternerKey);
}
/**
* 根据微信订单号查询退款
* @param appid 公众账号ID
* @param mch_id 商户号
* @param paternerKey 商户密钥
* @param out_trade_no 商户订单号
* @return map
*/
public static Map<String, String> refundQueryByOutTradeNo(String appid, String mch_id, String paternerKey, String out_trade_no) {
Map<String, String> params = new HashMap<String, String>();
params.put("out_trade_no", out_trade_no);
return baseRefundQuery(params, appid, mch_id, paternerKey);
}
/**
* 根据微信订单号查询退款
* @param appid 公众账号ID
* @param mch_id 商户号
* @param paternerKey 商户密钥
* @param out_refund_no 商户退款单号
* @return map
*/
public static Map<String, String> refundQueryByOutRefundNo(String appid, String mch_id, String paternerKey, String out_refund_no) {
Map<String, String> params = new HashMap<String, String>();
params.put("out_refund_no", out_refund_no);
return baseRefundQuery(params, appid, mch_id, paternerKey);
}
/**
* 根据微信订单号查询退款
* @param appid 公众账号ID
* @param mch_id 商户号
* @param paternerKey 商户密钥
* @param refund_id 微信退款单号
* @return map
*/
public static Map<String, String> refundQueryByRefundId(String appid, String mch_id, String paternerKey, String refund_id) {
Map<String, String> params = new HashMap<String, String>();
params.put("refund_id", refund_id);
return baseRefundQuery(params, appid, mch_id, paternerKey);
}
private static String downloadBillUrl = "https://api.mch.weixin.qq.com/pay/downloadbill";
/**
* <pre>
* ALL,返回当日所有订单信息,默认值
* SUCCESS,返回当日成功支付的订单
* REFUND,返回当日退款订单
* REVOKED,已撤销的订单
* </pre>
*/
public static enum BillType {
ALL, SUCCESS, REFUND, REVOKED
}
/**
* 下载对账单
* <pre>
* 公众账号ID appid 是 String(32) wx8888888888888888 微信分配的公众账号ID(企业号corpid即为此appId)
* 商户号 mch_id 是 String(32) 1900000109 微信支付分配的商户号
* 设备号 device_info 否 String(32) 013467007045764 微信支付分配的终端设备号
* 随机字符串 nonce_str 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位。推荐随机数生成算法
* 签名 sign 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名生成算法
* 对账单日期 bill_date 是 String(8) 20140603 下载对账单的日期,格式:20140603
* 账单类型 bill_type 否 String(8)
* </pre>
* @param appid 公众账号ID
* @param mch_id 商户号
* @param paternerKey 签名密匙
* @param billDate 对账单日期
* @return String
*/
public static Map<String,String> downloadBill(String appid, String mch_id, String paternerKey, String billDate) {
return downloadBill(appid, mch_id, paternerKey, billDate, null);
}
/**
* 下载对账单
* <pre>
* 公众账号ID appid 是 String(32) wx8888888888888888 微信分配的公众账号ID(企业号corpid即为此appId)
* 商户号 mch_id 是 String(32) 1900000109 微信支付分配的商户号
* 设备号 device_info 否 String(32) 013467007045764 微信支付分配的终端设备号
* 随机字符串 nonce_str 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位。推荐随机数生成算法
* 签名 sign 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,详见签名生成算法
* 对账单日期 bill_date 是 String(8) 20140603 下载对账单的日期,格式:20140603
* 账单类型 bill_type 否 String(8)
* </pre>
* @param appid 公众账号ID
* @param mch_id 商户号
* @param paternerKey 签名密匙
* @param billDate 对账单日期
* @param billType 账单类型
* @return String
*/
public static Map<String,String> downloadBill(String appid, String mch_id, String paternerKey, String billDate, BillType billType) {
Map<String, String> params = new HashMap<String, String>();
params.put("appid", appid);
params.put("mch_id", mch_id);
params.put("nonce_str", System.currentTimeMillis() + "");
params.put("bill_date", billDate);
if (null != billType) {
params.put("bill_type", billType.name());
} else {
params.put("bill_type", BillType.ALL.name());
}
String sign = PaymentKit.createSign(params, paternerKey);
params.put("sign", sign);
String str = HttpKit.post(downloadBillUrl, PaymentKit.toXml(params));
return PaymentKit.xmlToMap(str);
}
}
- 微信支付的统一下单工具类
package com.hn.yuan.wxinterface;
import org.apache.commons.io.Charsets;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
/**
* 微信支付的统一下单工具类
* @author XIAOCAO
* @version 2020/12/24
**/
public class PaymentKit {
/**
* 组装签名的字段
* @param params 参数
* @param urlEncoder 是否urlEncoder
* @return String
*/
public static String packageSign(Map<String, String> params, boolean urlEncoder) {
// 先将参数以其参数名的字典序升序进行排序
TreeMap<String, String> sortedParams = new TreeMap<String, String>(params);
// 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Entry<String, String> param : sortedParams.entrySet()) {
String value = param.getValue();
if (Tools.isEmpty(value)) {
continue;
}
if (first) {
first = false;
} else {
sb.append("&");
}
sb.append(param.getKey()).append("=");
if (urlEncoder) {
try { value = urlEncode(value); } catch (UnsupportedEncodingException e) {}
}
sb.append(value);
}
return sb.toString();
}
/**
* urlEncode
* @param src 微信参数
* @return String
* @throws UnsupportedEncodingException 编码错误
*/
public static String urlEncode(String src) throws UnsupportedEncodingException {
return URLEncoder.encode(src, Charsets.UTF_8.name()).replace("+", "%20");
}
/**
* 生成签名
* @param params 参数
* @param paternerKey 支付密钥
* @return sign
*/
public static String createSign(Map<String, String> params, String paternerKey) {
// 生成签名前先去除sign
params.remove("sign");
String stringA = packageSign(params, false);
String stringSignTemp = stringA + "&key=" + paternerKey;
return Tools.md5(stringSignTemp).toUpperCase();
}
/**
* 支付异步通知时校验sign
* @param params 参数
* @param paternerKey 支付密钥
* @return {boolean}
*/
public static boolean verifyNotify(Map<String, String> params, String paternerKey){
String sign = params.get("sign");
String localSign = PaymentKit.createSign(params, paternerKey);
return sign.equals(localSign);
}
/**
* 微信下单,map to xml
* @param params 参数
* @return String
*/
public static String toXml(Map<String, String> params) {
StringBuilder xml = new StringBuilder();
xml.append("<xml>");
for (Entry<String, String> entry : params.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
// 略过空值
if (Tools.isEmpty(value)) continue;
xml.append("<").append(key).append(">");
xml.append(entry.getValue());
xml.append("</").append(key).append(">");
}
xml.append("</xml>");
return xml.toString();
}
/**
* 针对支付的xml,没有嵌套节点的简单处理
* @param xmlStr xml字符串
* @return map集合
*/
public static Map<String, String> xmlToMap(String xmlStr) {
XmlHelper xmlHelper = XmlHelper.of(xmlStr);
return xmlHelper.toMap();
}
}
- 支付签名常用类
package com.hn.yuan.wxinterface;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.apache.commons.codec.digest.DigestUtils;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;
/**
* 支付签名常用类
* @author XIAOCAO
* @version 2020/12/24
**/
public class PayUtil {
/**
* 签名字符串
*
* @param text 需要签名的字符串
* @param key 密钥
* @param input_charset 编码格式
* @return 签名结果
*/
public static String sign(String text, String key, String input_charset) {
text = text + "&key=" + key;
return DigestUtils.md5Hex(getContentBytes(text, input_charset));
}
/**
* 签名字符串
*
* @param text 需要签名的字符串
* @param sign 签名结果
* @param key 密钥
* @param input_charset 编码格式
* @return 签名结果
*/
public static boolean verify(String text, String sign, String key, String input_charset) {
text = text +"&key=" + key;
String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset)).toUpperCase();
if (mysign.equals(sign)) {
return true;
} else {
return false;
}
}
/**
* @param content
* @param charset
* @return
* @throws java.security.SignatureException
* @throws UnsupportedEncodingException
*/
public static byte[] getContentBytes(String content, String charset) {
if (charset == null || "".equals(charset)) {
return content.getBytes();
}
try {
return content.getBytes(charset);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("MD5签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
}
}
private static boolean isValidChar(char ch) {
if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
return true;
if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))
return true;// 简体中文汉字编码
return false;
}
/**
* 除去数组中的空值和签名参数
*
* @param sArray 签名参数组
* @return 去掉空值与签名参数后的新签名参数组
*/
public static Map<String, String> paraFilter(Map<String, String> sArray) {
Map<String, String> result = new HashMap<String, String>();
if (sArray == null || sArray.size() <= 0) {
return result;
}
for (String key : sArray.keySet()) {
String value = sArray.get(key);
if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
|| key.equalsIgnoreCase("sign_type")) {
continue;
}
result.put(key, value);
}
return result;
}
/**
* 把数组所有元素排序,并按照“参数=参数值”的模式用“&”字符拼接成字符串
*
* @param params 需要排序并参与字符拼接的参数组
* @return 拼接后字符串
*/
public static String createLinkString(Map<String, String> params) {
List<String> keys = new ArrayList<>(params.keySet());
Collections.sort(keys);
String prestr = "";
for (int i = 0; i < keys.size(); i++) {
String key = keys.get(i);
String value = params.get(key);
if (i == keys.size() - 1) {// 拼接时,不包括最后一个&字符
prestr = prestr + key + "=" + value;
} else {
prestr = prestr + key + "=" + value + "&";
}
}
return prestr;
}
/**
* @param requestUrl 请求地址
* @param requestMethod 请求方法
* @param outputStr 参数
*/
public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
// 创建SSLContext
StringBuffer buffer = null;
try {
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(requestMethod);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.connect();
//往服务器端写内容
if (null != outputStr) {
OutputStream os = conn.getOutputStream();
os.write(outputStr.getBytes("utf-8"));
os.close();
}
// 读取服务器端返回的内容
InputStream is = conn.getInputStream();
InputStreamReader isr = new InputStreamReader(is, "utf-8");
BufferedReader br = new BufferedReader(isr);
buffer = new StringBuffer();
String line = null;
while ((line = br.readLine()) != null) {
buffer.append(line);
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
return buffer.toString();
}
public static String urlEncodeUTF8(String source) {
String result = source;
try {
result = java.net.URLEncoder.encode(source, "UTF-8");
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return result;
}
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
*
* @param strxml
* @return
* @throws org.jdom2.JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws Exception {
if (null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = String2Inputstream(strxml);
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
/**
* 获取子结点的xml
*
* @param children
* @return String
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if (!list.isEmpty()) {
sb.append(getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
public static InputStream String2Inputstream(String str) {
return new ByteArrayInputStream(str.getBytes());
}
/**
* java对象转字符串
* @param <T> 泛型占位符
* @param obj T
* @return String
*/
public static <T> String toJson(T obj) {
Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").enableComplexMapKeySerialization().create();
return gson.toJson(obj);
}
}
- 支付常量
package com.hn.yuan.wxinterface;
/**
* 支付常量
*
* @author XIAOCAO
* @version 2020/12/24
**/
public class WXPayConstants {
public enum SignType {
MD5, HMACSHA256
}
public static final String DOMAIN_API = "api.mch.weixin.qq.com";
public static final String DOMAIN_API2 = "api2.mch.weixin.qq.com";
public static final String DOMAIN_APIHK = "apihk.mch.weixin.qq.com";
public static final String DOMAIN_APIUS = "apius.mch.weixin.qq.com";
public static final String FAIL = "FAIL";
/**
* 成功的标识
*/
public static final String SUCCESS = "SUCCESS";
/**
* 返回状态码的变量名
*/
public static final String RETURN_CODE = "return_code";
public static final String HMACSHA256 = "HMAC-SHA256";
public static final String MD5 = "MD5";
public static final String FIELD_SIGN = "sign";
public static final String FIELD_SIGN_TYPE = "sign_type";
public static final String MICROPAY_URL_SUFFIX = "/pay/micropay";
public static final String UNIFIEDORDER_URL_SUFFIX = "/pay/unifiedorder";
public static final String ORDERQUERY_URL_SUFFIX = "/pay/orderquery";
public static final String REVERSE_URL_SUFFIX = "/secapi/pay/reverse";
public static final String CLOSEORDER_URL_SUFFIX = "/pay/closeorder";
public static final String REFUNDQUERY_URL_SUFFIX = "/pay/refundquery";
public static final String DOWNLOADBILL_URL_SUFFIX = "/pay/downloadbill";
// sandbox
public static final String SANDBOX_MICROPAY_URL_SUFFIX = "/sandboxnew/pay/micropay";
public static final String SANDBOX_UNIFIEDORDER_URL_SUFFIX = "/sandboxnew/pay/unifiedorder";
public static final String SANDBOX_ORDERQUERY_URL_SUFFIX = "/sandboxnew/pay/orderquery";
public static final String SANDBOX_REVERSE_URL_SUFFIX = "/sandboxnew/secapi/pay/reverse";
public static final String SANDBOX_CLOSEORDER_URL_SUFFIX = "/sandboxnew/pay/closeorder";
public static final String SANDBOX_REFUND_URL_SUFFIX = "/sandboxnew/secapi/pay/refund";
public static final String SANDBOX_REFUNDQUERY_URL_SUFFIX = "/sandboxnew/pay/refundquery";
public static final String SANDBOX_DOWNLOADBILL_URL_SUFFIX = "/sandboxnew/pay/downloadbill";
public static final String SANDBOX_REPORT_URL_SUFFIX = "/sandboxnew/payitil/report";
public static final String SANDBOX_SHORTURL_URL_SUFFIX = "/sandboxnew/tools/shorturl";
public static final String SANDBOX_AUTHCODETOOPENID_URL_SUFFIX = "/sandboxnew/tools/authcodetoopenid";
}
- xpath解析xml
package com.hn.yuan.wxinterface;
import org.apache.commons.io.IOUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
/**
* xpath解析xml
* <pre>
* 文档地址:
* http://www.w3school.com.cn/xpath/index.asp
* </pre>
* @author XIAOCAO
* @version 2020/12/24
**/
public class XmlHelper {
private final XPath path;
private final Document doc;
private XmlHelper(InputSource inputSource) throws ParserConfigurationException, SAXException, IOException {
DocumentBuilderFactory dbf = getDocumentBuilderFactory();
DocumentBuilder db = dbf.newDocumentBuilder();
doc = db.parse(inputSource);
path = getXPathFactory().newXPath();
}
private static XmlHelper create(InputSource inputSource) {
try {
return new XmlHelper(inputSource);
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} catch (SAXException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static XmlHelper of(InputStream is) {
InputSource inputSource = new InputSource(is);
return create(inputSource);
}
public static XmlHelper of(String xmlStr) {
StringReader sr = new StringReader(xmlStr.trim());
InputSource inputSource = new InputSource(sr);
XmlHelper xmlHelper = create(inputSource);
IOUtils.closeQuietly(sr);
return xmlHelper;
}
private Object evalXPath(String expression, Object item, QName returnType) {
item = null == item ? doc : item;
try {
return path.evaluate(expression, item, returnType);
} catch (XPathExpressionException e) {
throw new RuntimeException(e);
}
}
/**
* 获取String
* @param expression 路径
* @return String
*/
public String getString(String expression) {
return (String) evalXPath(expression, null, XPathConstants.STRING);
}
/**
* 获取Boolean
* @param expression 路径
* @return String
*/
public Boolean getBoolean(String expression) {
return (Boolean) evalXPath(expression, null, XPathConstants.BOOLEAN);
}
/**
* 获取Number
* @param expression 路径
* @return {Number}
*/
public Number getNumber(String expression) {
return (Number) evalXPath(expression, null, XPathConstants.NUMBER);
}
/**
* 获取某个节点
* @param expression 路径
* @return {Node}
*/
public Node getNode(String expression) {
return (Node) evalXPath(expression, null, XPathConstants.NODE);
}
/**
* 获取子节点
* @param expression 路径
* @return NodeList
*/
public NodeList getNodeList(String expression) {
return (NodeList) evalXPath(expression, null, XPathConstants.NODESET);
}
/**
* 获取String
* @param node 节点
* @param expression 相对于node的路径
* @return String
*/
public String getString(Object node, String expression) {
return (String) evalXPath(expression, node, XPathConstants.STRING);
}
/**
* 获取
* @param node 节点
* @param expression 相对于node的路径
* @return String
*/
public Boolean getBoolean(Object node, String expression) {
return (Boolean) evalXPath(expression, node, XPathConstants.BOOLEAN);
}
/**
* 获取
* @param node 节点
* @param expression 相对于node的路径
* @return {Number}
*/
public Number getNumber(Object node, String expression) {
return (Number) evalXPath(expression, node, XPathConstants.NUMBER);
}
/**
* 获取某个节点
* @param node 节点
* @param expression 路径
* @return {Node}
*/
public Node getNode(Object node, String expression) {
return (Node) evalXPath(expression, node, XPathConstants.NODE);
}
/**
* 获取子节点
* @param node 节点
* @param expression 相对于node的路径
* @return NodeList
*/
public NodeList getNodeList(Object node, String expression) {
return (NodeList) evalXPath(expression, node, XPathConstants.NODESET);
}
/**
* 针对没有嵌套节点的简单处理
* @return map集合
*/
public Map<String, String> toMap() {
Element root = doc.getDocumentElement();
Map<String, String> params = new HashMap<String, String>();
// 将节点封装成map形式
NodeList list = root.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
if (node instanceof Element) {
params.put(node.getNodeName(), node.getTextContent());
}
}
return params;
}
private static DocumentBuilderFactory getDocumentBuilderFactory(){
return XmlHelperHolder.documentBuilderFactory;
}
private static XPathFactory getXPathFactory() {
return XmlHelperHolder.xPathFactory;
}
/**
* 内部类单例
*/
private static class XmlHelperHolder {
private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
private static XPathFactory xPathFactory = XPathFactory.newInstance();
}
}
- 常用工具类
package com.hn.yuan.wxinterface;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.security.MessageDigest;
import java.util.*;
/**
* 常用工具类
* @author XIAOCAO
* @version 2020/12/24
**/
public class WXPayUtil {
/**
* XML格式字符串转换为Map
*
* @param strXML XML字符串
* @return XML数据转换后的Map
* @throws Exception
*/
public static Map<String, String> xmlToMap(String strXML) throws Exception {
try {
Map<String, String> data = new HashMap<String, String>();
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
org.w3c.dom.Document doc = documentBuilder.parse(stream);
doc.getDocumentElement().normalize();
NodeList nodeList = doc.getDocumentElement().getChildNodes();
for (int idx = 0; idx < nodeList.getLength(); ++idx) {
Node node = nodeList.item(idx);
if (node.getNodeType() == Node.ELEMENT_NODE) {
org.w3c.dom.Element element = (org.w3c.dom.Element) node;
data.put(element.getNodeName(), element.getTextContent());
}
}
try {
stream.close();
} catch (Exception ex) {
// do nothing
}
return data;
} catch (Exception ex) {
WXPayUtil.getLogger().warn("Invalid XML, can not convert to map. Error message: {}. XML content: {}", ex.getMessage(), strXML);
throw ex;
}
}
/**
* 将Map转换为XML格式的字符串
*
* @param data Map类型数据
* @return XML格式的字符串
* @throws Exception
*/
public static String mapToXml(Map<String, String> data) throws Exception {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
org.w3c.dom.Document document = documentBuilder.newDocument();
org.w3c.dom.Element root = document.createElement("xml");
document.appendChild(root);
for (String key: data.keySet()) {
String value = data.get(key);
if (value == null) {
value = "";
}
value = value.trim();
org.w3c.dom.Element filed = document.createElement(key);
filed.appendChild(document.createTextNode(value));
root.appendChild(filed);
}
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
DOMSource source = new DOMSource(document);
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StringWriter writer = new StringWriter();
StreamResult result = new StreamResult(writer);
transformer.transform(source, result);
String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
try {
writer.close();
}
catch (Exception ex) {
}
return output;
}
/**
* 生成带有 sign 的 XML 格式字符串
*
* @param data Map类型数据
* @param key API密钥
* @return 含有sign字段的XML
*/
public static String generateSignedXml(final Map<String, String> data, String key) throws Exception {
return generateSignedXml(data, key, WXPayConstants.SignType.MD5);
}
/**
* 生成带有 sign 的 XML 格式字符串
*
* @param data Map类型数据
* @param key API密钥
* @param signType 签名类型
* @return 含有sign字段的XML
*/
public static String generateSignedXml(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
String sign = generateSignature(data, key, signType);
data.put(WXPayConstants.FIELD_SIGN, sign);
return mapToXml(data);
}
/**
* 判断签名是否正确
*
* @param xmlStr XML格式数据
* @param key API密钥
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(String xmlStr, String key) throws Exception {
Map<String, String> data = xmlToMap(xmlStr);
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key).equals(sign);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。使用MD5签名。
*
* @param data Map类型数据
* @param key API密钥
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key) throws Exception {
return isSignatureValid(data, key, WXPayConstants.SignType.MD5);
}
/**
* 判断签名是否正确,必须包含sign字段,否则返回false。
*
* @param data Map类型数据
* @param key API密钥
* @param signType 签名方式
* @return 签名是否正确
* @throws Exception
*/
public static boolean isSignatureValid(Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
if (!data.containsKey(WXPayConstants.FIELD_SIGN) ) {
return false;
}
String sign = data.get(WXPayConstants.FIELD_SIGN);
return generateSignature(data, key, signType).equals(sign);
}
/**
* 生成签名
*
* @param data 待签名数据
* @param key API密钥
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key) throws Exception {
return generateSignature(data, key, WXPayConstants.SignType.MD5);
}
/**
* 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。
*
* @param data 待签名数据
* @param key API密钥
* @param signType 签名方式
* @return 签名
*/
public static String generateSignature(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
Set<String> keySet = data.keySet();
String[] keyArray = keySet.toArray(new String[keySet.size()]);
Arrays.sort(keyArray);
StringBuilder sb = new StringBuilder();
for (String k : keyArray) {
if (k.equals(WXPayConstants.FIELD_SIGN)) {
continue;
}
if(data.get(k).trim().length() > 0) // 参数值为空,则不参与签名
sb.append(k).append("=").append(data.get(k).trim()).append("&");
}
sb.append("key=").append(key);
if (WXPayConstants.SignType.MD5.equals(signType)) {
return MD5(sb.toString()).toUpperCase();
}
else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {
return HMACSHA256(sb.toString(), key);
}
else {
throw new Exception(String.format("Invalid sign_type: %s", signType));
}
}
/**
* 获取随机字符串 Nonce Str
*
* @return String 随机字符串
*/
public static String generateNonceStr() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
/**
* 生成 MD5
*
* @param data 待处理数据
* @return MD5结果
*/
public static String MD5(String data) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 生成 HMACSHA256
* @param data 待处理数据
* @param key 密钥
* @return 加密结果
* @throws Exception
*/
public static String HMACSHA256(String data, String key) throws Exception {
Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");
sha256_HMAC.init(secret_key);
byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
}
/**
* 日志
* @return
*/
public static Logger getLogger() {
Logger logger = LoggerFactory.getLogger("wxpay java sdk");
return logger;
}
/**
* 获取当前时间戳,单位秒
* @return
*/
public static long getCurrentTimestamp() {
return System.currentTimeMillis()/1000;
}
/**
* 获取当前时间戳,单位毫秒
* @return
*/
public static long getCurrentTimestampMs() {
return System.currentTimeMillis();
}
/**
* 生成 uuid, 即用来标识一笔单,也用做 nonce_str
* @return
*/
public static String generateUUID() {
return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
}
}
- Tools工具类
package com.hn.yuan.wxinterface;
import java.security.MessageDigest;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 常用工具
* @author XIAOCAO
* @version 2020/12/24
**/
public class Tools {
/**
* 随机生成六位数验证码
* @return
*/
public static int getRandomNum(){
Random r = new Random();
return r.nextInt(900000)+100000;//(Math.random()*(999999-100000)+100000)
}
/**
* 检测字符串是否不为空(null,"","null")
* @param s
* @return 不为空则返回true,否则返回false
*/
public static boolean notEmpty(String s){
return s!=null && !"".equals(s) && !"null".equals(s);
}
/**
* 检测字符串是否为空(null,"","null")
* @param s
* @return 为空则返回true,不否则返回false
*/
public static boolean isEmpty(String s){
return s==null || "".equals(s) || "null".equals(s);
}
/**
* 字符串转换为字符串数组
* @param str 字符串
* @param splitRegex 分隔符
* @return
*/
public static String[] str2StrArray(String str,String splitRegex){
if(isEmpty(str)){
return null;
}
return str.split(splitRegex);
}
/**
* 用默认的分隔符(,)将字符串转换为字符串数组
* @param str 字符串
* @return
*/
public static String[] str2StrArray(String str){
return str2StrArray(str,",\\s*");
}
/**
* 按照yyyy-MM-dd HH:mm:ss的格式,日期转字符串
* @param date
* @return yyyy-MM-dd HH:mm:ss
*/
public static String date2Str(Date date){
return date2Str(date,"yyyy-MM-dd HH:mm:ss");
}
/**
* 按照yyyy-MM-dd HH:mm:ss的格式,字符串转日期
* @param date
* @return
*/
public static Date str2Date(String date){
if(notEmpty(date)){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
return sdf.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
return new Date();
}else{
return null;
}
}
/**
* 按照参数format的格式,日期转字符串
* @param date
* @param format
* @return
*/
public static String date2Str(Date date,String format){
if(date!=null){
SimpleDateFormat sdf = new SimpleDateFormat(format);
return sdf.format(date);
}else{
return "";
}
}
/**
* 把时间根据时、分、秒转换为时间段
* @param StrDate
*/
public static String getTimes(String StrDate){
String resultTimes = "";
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date now;
try {
now = new Date();
Date date=df.parse(StrDate);
long times = now.getTime()-date.getTime();
long day = times/(24*60*60*1000);
long hour = (times/(60*60*1000)-day*24);
long min = ((times/(60*1000))-day*24*60-hour*60);
long sec = (times/1000-day*24*60*60-hour*60*60-min*60);
StringBuffer sb = new StringBuffer();
//sb.append("发表于:");
if(hour>0 ){
sb.append(hour+"小时前");
} else if(min>0){
sb.append(min+"分钟前");
} else{
sb.append(sec+"秒前");
}
resultTimes = sb.toString();
} catch (ParseException e) {
e.printStackTrace();
}
return resultTimes;
}
/**
* 验证邮箱
* @param email
* @return
*/
public static boolean checkEmail(String email){
boolean flag = false;
try{
String check = "^([a-z0-9A-Z]+[-|_|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
Pattern regex = Pattern.compile(check);
Matcher matcher = regex.matcher(email);
flag = matcher.matches();
}catch(Exception e){
flag = false;
}
return flag;
}
/**
* 验证手机号码
* @return
*/
public static boolean checkMobileNumber(String mobileNumber){
Pattern p = null;
Matcher m = null;
boolean b = false;
p = Pattern.compile("^[1][3,4,5,7,8][0-9]{9}$"); // 验证手机号
m = p.matcher(mobileNumber);
b = m.matches();
return b;
}
/**
* 将驼峰转下划线
* @param param
* @return
*/
public static String camelToUnderline(String param){
if (param==null||"".equals(param.trim())){
return "";
}
int len=param.length();
StringBuilder sb=new StringBuilder(len);
for (int i = 0; i < len; i++) {
char c=param.charAt(i);
if (Character.isUpperCase(c)){
sb.append("_");
sb.append(Character.toLowerCase(c));
}else{
sb.append(c);
}
}
return sb.toString();
}
/**
* 去掉下划线并将下划线后的首字母转为大写
* @param str
* @return
*/
public static String transformStr(String str){
//去掉数据库字段的下划线
if(str.contains("_")) {
String[] names = str.split("_");
String firstPart = names[0];
String otherPart = "";
for (int i = 1; i < names.length; i++) {
String word = names[i].replaceFirst(names[i].substring(0, 1), names[i].substring(0, 1).toUpperCase());
otherPart += word;
}
str = firstPart + otherPart;
}
return str;
}
/**
* 转换为map
* @param list
* @return
*/
public static List<Map<String,Object>> transformMap(List<Map<String,Object>> list){
List<Map<String,Object>> resultMapList = new ArrayList<>();
for (Map<String, Object> map : list) {
Map<String,Object> tempMap = new HashMap<>();
for (String s : map.keySet()) {
tempMap.put(transformStr(s),map.get(s));
}
resultMapList.add(tempMap);
}
return resultMapList;
}
public static String clearHtml(String content,int p) {
if(null==content) return "";
if(0==p) return "";
Pattern p_script;
Matcher m_script;
Pattern p_style;
Matcher m_style;
Pattern p_html;
Matcher m_html;
try {
String regEx_script = "<[\\s]*?script[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?script[\\s]*?>";
//定义script的正则表达式{或<script[^>]*?>[\\s\\S]*?<\\/script> }
String regEx_style = "<[\\s]*?style[^>]*?>[\\s\\S]*?<[\\s]*?\\/[\\s]*?style[\\s]*?>";
//定义style的正则表达式{或<style[^>]*?>[\\s\\S]*?<\\/style> }
String regEx_html = "<[^>]+>"; //定义HTML标签的正则表达式
p_script = Pattern.compile(regEx_script,Pattern.CASE_INSENSITIVE);
m_script = p_script.matcher(content);
content = m_script.replaceAll(""); //过滤script标签
p_style = Pattern.compile(regEx_style,Pattern.CASE_INSENSITIVE);
m_style = p_style.matcher(content);
content = m_style.replaceAll(""); //过滤style标签
p_html = Pattern.compile(regEx_html,Pattern.CASE_INSENSITIVE);
m_html = p_html.matcher(content);
content = m_html.replaceAll(""); //过滤html标签
}catch(Exception e) {
return "";
}
if(content.length()>p){
content = content.substring(0, p)+"...";
}else{
content = content + "...";
}
return content;
}
public static String md5(String str) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(str.getBytes());
byte b[] = md.digest();
int i;
StringBuffer buf = new StringBuffer("");
for (int offset = 0; offset < b.length; offset++) {
i = b[offset];
if (i < 0)
i += 256;
if (i < 16)
buf.append("0");
buf.append(Integer.toHexString(i));
}
str = buf.toString();
} catch (Exception e) {
e.printStackTrace();
}
return str;
}
}
微信小程序支付功能完整流程-总结(可忽略)
获取令牌token
- 获取令牌token
// 点击支付
async handleOrderPay() {
// 从缓存中获取token
const token = wx.getStorageSync('token')
// 如果token不存在,跳转到授权页面获取token
if (!token) {
wx.navigateTo({
url: '/pages/auth/auth',
})
}
<!-- 点击按钮获取用户信息 -->
<button type="primary" plain open-type="getUserInfo" bindgetuserinfo="handleGetUserInfo">授权</button>
- 缓存中不存在token,授权!
// 授权操作
// pages/auth/auth.js
import {request} from '../../request/index.js' //网络请求
import regeneratorRuntime from '../../lib/runtime/runtime' //使用es7语法
import {login} from '../../utils/asyncWx.js' //内部封装了wx.login(),通过该方法获取一个必须参数code
Page({
// 获取用户信息
async handleGetUserInfo(e) {
try {
// 获取小程序登录成功后的五个必须参数
const {
encryptedData,
rawData,
iv,
signature
} = e.detail
const {
code
} = await login()
// 将这五个参数存入loginParams自定义对象中
const loginParams = {
encryptedData,
rawData,
iv,
signature,
code
}
// 获取token
const {
token
} = await request({
url: '/users/wxlogin',
// 传入这五个必须参数
data: loginParams,
method: 'POST'
})
// 将token存入缓存中,同时跳转到上一个页面
wx.setStorageSync('token', token)
wx.navigateBack({
// delta表示返回的层级,1表示返回上一层,2表示返回上两层
delta: 1,
})
} catch (error) {
console.log(error)
}
}
})
创建订单
- 获取到5个必须参数
// 创建订单
// 请求头参数,这个参数会贯穿整个支付过程中的网络请求
const header = {
// 授权参数即为token
Authorization: token
}
// 请求体参数
const order_price = this.data.totalPrice //总价格
const consignee_addr = this.data.address.all //详细收获地址
const cart = this.data.cart
let goods = [] //订单数组,从购物车中提取出几个所需要的属性组成的数组
goods = cart.forEach(v => {
goods_id: v.goods_id
goods_number: v.num
goods_price: v.goods_price
})
// 将所需要的订单参数存入orderParams
const orderParams = {
order_price,
consignee_addr,
goods
}
- 获取订单编号
// 获取订单编号
const {order_number} = await request({
url: '/my/orders/create',
method: 'POST',
// 将之前获得的订单参数传入
data: orderParams,
header
})
console.log(order_number)
}
预支付
- 根据订单编号获取到支付参数对象pay
const {pay} = await request({
url: '/my/orders/req_unifiedorder',
method: "POST",
header,
// 传入订单编号
data: {
order_number
}
})
console.log('pay')
发起微信支付
- 将支付参数pay传入到requestPayment中即可
await requestPayment(pay)
收尾工作
- 跳转到订单页面,同时删除购物车中以购买的商品
await showToast('支付成功')
// 删除缓存中已经被选中的商品
let newCart = wx.getStorageSync('cart')
newCart = newCart.filter(v => !v.checked)
wx.setStorageSync('cart', newCart)
// 此时跳转到订单页面
wx.navigateTo({
url: '/pages/order/order',
})
后续V3版本-待编辑
各位看官》创作不易,点个赞!!!
诸君共勉:万事开头难,只愿肯放弃。
免责声明:本文章仅用于学习参考