JAVA-小程序微信支付
程序员文章站
2022-06-14 23:27:51
...
- -微信支付之小程序- -
哈喽 我是你们的KingYiFan,一直说把微信支付给分享出来一直没有机会。终于闲下来了。听着音乐给你们分享一下。不懂可以随时联系我。。
-~~ 本文介绍小程序微信支付的java服务端~~。
- 微信小程序支付文档:https://pay.weixin.qq.com/wiki/doc/api/wxa/wxa_api.php?chapter=7_3&index=1
- 编写之前请先浏览文档中的业务流程、API列表中的统一下单和支付结果通知。
- 调用需要用到小程序的APPID、商户号、API**。请参考微信支付文档申请和获取。
废话不多说直接上微信官方流程图 哈哈哈哈哈 是不是看的一脸懵逼。
–KingYiFan 给大家准备了一个自己画的流程图
/**
* @Title: 小程序微信支付
* @Description: 调用微信的支付接口 统一下单
* @author: KingYiFan
*/
@ResponseBody
@RequestMapping(value = "wxpayTest", method = RequestMethod.GET, produces ="application/json; charset=utf-8")
public void wxpayTest(HttpServletRequest request, HttpServletResponse response, Model model) {
try {
//前台传输一个openid--每个用户对应小程序都会生成一个独一无二的openid
String openid = request.getParameter("openid");
//创建一个实体类
OrderInfo order = new OrderInfo();
//appid 是小程序的appid(微信公众平台获取)
order.setAppid(Configure.getAppID());
//商户id(微信商户平台申请)
order.setMch_id(Configure.getMch_id());
//随机字符串,微信推荐长度要求在32位以内。
order.setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
//商品描述-可以随便写
order.setBody("KingYiFan-测试");
//商户订单号可以根据这个订单查询账单
order.setOut_trade_no(RandomStringGenerator.getRandomStringByLength(32));
//支付金额 微信支付是按分为单位的 是int类型 1就是1分 10就是一角
//这是一个坑
order.setTotal_fee(1);
//APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
//微信写的一团懵逼 到底是用户的ip还是我们服务器的ip
//算了 不管了就直接用我们服务器ip把
order.setSpbill_create_ip(Configure.getIp());
//微信支付回调地址 也就是用户支付成功改状态的地方
order.setNotify_url("https://wxxcx.jiajiao2o.com/v2/wxPayNotify");
//如果是微信小小程序和公众号这是一个固定值
order.setTrade_type("JSAPI");
//openid 我上面介绍过,这里不解释了。。。
order.setOpenid(openid);
//封装签名的格式 md5
order.setSign_type("MD5");
// 生成签名 一会我会把生成签名格式甩到下面
String sign = Signature.getSign(order);
//封装签名
order.setSign(sign);
//用httpclient 发送一个post请求微信官方Api
String result = HttpRequest.sendPost("https://api.mch.weixin.qq.com/pay/unifiedorder", order);
//返回一个预订单id
System.out.println(result);
//来回传输数据是xml格式的,然后返回结果也是xml格式的 解析xml
XStream xStream = new XStream();
xStream.alias("xml", OrderReturnInfo.class);
//获得微信返回的结果
OrderReturnInfo returnInfo = (OrderReturnInfo) xStream.fromXML(result);
// 进行二次签名
SignInfo signInfo = new SignInfo();
//appid 是小程序appid 上面有介绍
signInfo.setAppId(Configure.getAppID());
//当前时间戳
long time = System.currentTimeMillis() / 1000;
signInfo.setTimeStamp(String.valueOf(time));
//随机字符串 建议32位
signInfo.setNonceStr(RandomStringGenerator.getRandomStringByLength(32));
//预订单ip 上一次请求返回的id
signInfo.setRepay_id("prepay_id=" + returnInfo.getPrepay_id());
//签名加密方式
signInfo.setSignType("MD5");
// 封装参数返回小程序段
String sign2 = Signature.getSign(signInfo);
JSONObject json = new JSONObject();
json.put("timeStamp", signInfo.getTimeStamp());
json.put("nonceStr", signInfo.getNonceStr());
json.put("package", signInfo.getRepay_id());
json.put("signType", signInfo.getSignType());
json.put("paySign", sign2);
response.getWriter().append(json.toJSONString());
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
上面的代码只是调起来微信支付,下面我再说一下用户支付成功回调
/**
* @Description: 提交支付后的微信异步返回接口
* @author: KingYiFan
*/
@ResponseBody
@RequestMapping(value = "wxPayNotify", method = RequestMethod.GET, produces = "application/json; charset=utf-8")
public void wxPayNotify(HttpServletRequest request, HttpServletResponse response) {
String resXml = "";
Map<String, String> backxml = new HashMap<String, String>();
InputStream inStream;
try {
inStream = request.getInputStream();
ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = inStream.read(buffer)) != -1) {
outSteam.write(buffer, 0, len);
}
logger.error("微信支付----付款成功----");
outSteam.close();
inStream.close();
String result = new String(outSteam.toByteArray(), "utf-8");// 获取微信调用我们notify_url的返回信息
logger.error("微信支付----result----=" + result);
Map<String, String> map = XCXUtils.readStringXmlOut(result);
if (map.get("result_code").toString().equalsIgnoreCase("SUCCESS")) {
logger.error("微信支付----返回成功");
//必须验证微信官方回调过来的签名加密方式防止恶意请求
if (Signature.verifyWeixinNotify(map)) {
//书写自己的业务逻辑
}
// ------------------------------
// 处理业务完毕
// ------------------------------
BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream());
out.write(resXml.getBytes());
out.flush();
out.close();
}
} catch (IOException e) {
// TODO Auto-generated catch block
logger.error("支付回调发布异常:" + e);
e.printStackTrace();
}
}
是不是炒鸡简单哈哈哈哈哈 被我雷到了么?哈哈哈哈~~
–下面给大家分享一下我自己封装的实体类
/**
* 预订单 Bean
* @author KingYiFan
*/
public class OrderInfo {
private String appid;// 小程序ID
private String mch_id;// 商户号
private String nonce_str;// 随机字符串
private String sign_type;//签名类型
private String sign;// 签名
private String body;// 商品描述
private String out_trade_no;// 商户订单号
private int total_fee;// 标价金额 ,单位为分
private String spbill_create_ip;// 终端IP
private String notify_url;// 通知地址
private String trade_type;// 交易类型
private String openid;//用户标识
public String getSign_type() {
return sign_type;
}
public void setSign_type(String sign_type) {
this.sign_type = sign_type;
}
public String getOpenid() {
return openid;
}
public void setOpenid(String openid) {
this.openid = openid;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
public String getMch_id() {
return mch_id;
}
public void setMch_id(String mch_id) {
this.mch_id = mch_id;
}
public String getNonce_str() {
return nonce_str;
}
public void setNonce_str(String nonce_str) {
this.nonce_str = nonce_str;
}
public String getSign() {
return sign;
}
public void setSign(String sign) {
this.sign = sign;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getOut_trade_no() {
return out_trade_no;
}
public void setOut_trade_no(String out_trade_no) {
this.out_trade_no = out_trade_no;
}
public int getTotal_fee() {
return total_fee;
}
public void setTotal_fee(int total_fee) {
this.total_fee = total_fee;
}
public String getSpbill_create_ip() {
return spbill_create_ip;
}
public void setSpbill_create_ip(String spbill_create_ip) {
this.spbill_create_ip = spbill_create_ip;
}
public String getNotify_url() {
return notify_url;
}
public void setNotify_url(String notify_url) {
this.notify_url = notify_url;
}
public String getTrade_type() {
return trade_type;
}
public void setTrade_type(String trade_type) {
this.trade_type = trade_type;
}
}
/**
* 签名算
* @param o
* 要参与签名的数据对象
* @return 签名
* @throws IllegalAccessException
*/
public static String getSign(Object o) throws IllegalAccessException {
ArrayList<String> list = new ArrayList<String>();
Class cls = o.getClass();
Field[] fields = cls.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
if (f.get(o) != null && f.get(o) != "") {
String name = f.getName();
XStreamAlias anno = f.getAnnotation(XStreamAlias.class);
if (anno != null)
name = anno.value();
list.add(name + "=" + f.get(o) + "&");
}
}
int size = list.size();
String[] arrayToSort = list.toArray(new String[size]);
Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size; i++) {
sb.append(arrayToSort[i]);
}
String result = sb.toString();
result += "key=" + Configure.getKey();
System.out.println("签名数据:" + result);
result = MD5.MD5Encode(result).toUpperCase();
return result;
}
/**
* 签名信息
* @author KingYiFan
*
*/
public class SignInfo {
private String appId;//小程序ID
private String timeStamp;//时间戳
private String nonceStr;//随机串
@XStreamAlias("package")
private String repay_id;
private String signType;//签名方式
public String getAppId() {
return appId;
}
public void setAppId(String appId) {
this.appId = appId;
}
public String getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(String timeStamp) {
this.timeStamp = timeStamp;
}
public String getNonceStr() {
return nonceStr;
}
public void setNonceStr(String nonceStr) {
this.nonceStr = nonceStr;
}
public String getRepay_id() {
return repay_id;
}
public void setRepay_id(String repay_id) {
this.repay_id = repay_id;
}
public String getSignType() {
return signType;
}
public void setSignType(String signType) {
this.signType = signType;
}
}
生成随机数的工具类
/**
* 获取一定长度的随机字符串
* @param length 指定字符串长度
* @return 一定长度的字符串
* @author KingYiFan
*/
public static String getRandomStringByLength(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
}
httpclient 发送post工具类
//连接超时时间,默认10秒
private static final int socketTimeout = 10000;
//传输超时时间,默认30秒
private static final int connectTimeout = 30000;
/**
* post请求
* @throws IOException
* @throws ClientProtocolException
* @throws NoSuchAlgorithmException
* @throws KeyStoreException
* @throws KeyManagementException
* @throws UnrecoverableKeyException
*/
public static String sendPost(String url, Object xmlObj) throws Exception {
HttpPost httpPost = new HttpPost(url);
//解决XStream对出现双下划线的bug
XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
xStreamForRequestPostData.alias("xml", xmlObj.getClass());
//将要提交给API的数据对象转换成XML格式数据Post给API
String postDataXML = xStreamForRequestPostData.toXML(xmlObj);
//得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别
StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(postEntity);
//设置请求器的配置
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
httpPost.setConfig(requestConfig);
HttpClient httpClient = HttpClients.createDefault();
HttpResponse response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
String result = EntityUtils.toString(entity, "UTF-8");
return result;
}
微信支付回调认证是否是微信官方请求工具类
/**
* 验证回调是否是微信官方
* @param map
* @return
* KingYiFan
*/
public static boolean verifyWeixinNotify(Map<String, String> map) {
SortedMap<String, Object> parameterMap = new TreeMap<String, Object>();
String sign = (String) map.get("sign");
for (Object keyValue : map.keySet()) {
if (!keyValue.toString().equals("sign")) {
parameterMap.put(keyValue.toString(), map.get(keyValue).toString());
}
}
String createSign = getSign(parameterMap);
if (createSign.equals(sign)) {
return true;
} else {
return false;
}
}
xml转换map工具类
/**
* @description 将xml字符串转换成map
* @param xml
* @return Map
*/
public static Map<String, String> readStringXmlOut(String xml) {
Map<String, String> map = new HashMap<String, String>();
Document doc = null;
try {
doc = DocumentHelper.parseText(xml); // 将字符串转为XML
Element rootElt = doc.getRootElement(); // 获取根节点
List<Element> list = rootElt.elements();// 获取根节点下所有节点
for (Element element : list) { // 遍历节点
map.put(element.getName(), element.getText()); // 节点的name为map的key,text为map的value
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return map;
}
上面就是一套完整的微信支付服务器端流程,有什么不懂就联系我。
鼓励作者写出更好的技术文档,就请我喝一瓶哇哈哈哈哈哈哈哈。。
微信:
支付宝:
感谢一路支持我的人。。。。。
Love me and hold me
QQ:69673804(16年老号)
EMAIL:[email protected]
友链交换
如果有兴趣和本博客交换友链的话,请按照下面的格式在评论区进行评论,我会尽快添加上你的链接。
网站名称:KingYiFan’S Blog
网站地址:http://blog.cnbuilder.cn
网站描述:年少是你未醒的梦话,风华是燃烬的彼岸花。
网站Logo/头像:http://blog.cnbuilder.cn/upload/2018/7/avatar20180720144536200.jpg
上一篇: vavr-用户手册