java实现微信App支付服务端

网友投稿 199 2023-01-21


java实现微信App支付服务端

微信App支付服务端的实现方法,供大家参考,具体内容如下

引言

主要实现app支付统一下单、异步通知、调起支付接口、支付订单查询、申请退款、查询退款功能;封装了https对发起退款的证书校验、签名、xml解析等。

支付流程

具体支付流程参考“微信APP”文档,文档地址

APP支付:APP端点击下单—-服务端生成订单,并调起“统一下单”,返回app支付所需参数—–APP端“调起支付接口“,发起支付—-微信服务器端调用服务端回调地址—–服务端按照“支付结果通知”,处理支付结果

app查询:调起“查询订单”

APP退款:发起退款请求,调用“申请退款”,发起退款,需双向证书验证

APP退款查询:调起“查询退款”

支付代码实现

代码实现签名、证书校验、http和https封装等,项目结构如下:

支付代码

包含支付、支付查询、异步通知、退款申请、退款查询

package org.andy.wxpay.controller;

import java.math.BigDecimal;

import java.util.HashMap;

import java.util.Map;

import javax.servlet.ServletInputStream;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.andy.wxpay.model.jsonResult;

import org.andy.wxpay.model.ResponseData;

import org.andy.wxpay.utils.CollectionUtil;

import org.andy.wxpay.utils.ConfigUtil;

import org.andy.wxpay.utils.FileUtil;

import org.andy.wxpay.utils.HttpUtils;

import org.andy.wxpay.utils.PayUtil;

import org.andy.wxpay.utils.SerializerFeatureUtil;

import org.andy.wxpay.utils.StringUtil;

import org.andy.wxpay.utils.WebUtil;

import org.andy.wxpay.utils.XmlUtil;

import org.apache.log4j.Logger;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.RequestParam;

import com.alibaba.fastjson.JSON;

/**

* 创建时间:2016年11月2日 下午4:16:32

*

* @author andy

* @version 2.2

*/

@Controller

@RequestMapping("/order")

public class PayController {

private static final Logger LOG = Logger.getLogger(PayController.class);

private static final String ORDER_PAY = "https://api.mch.weixin.qq.com/pay/unifiedorder"; // 统一下单

private static final String ORDER_PAY_QUERY = "https://api.mch.weixin.qq.com/pay/orderquery"; // 支付订单查询

private static final String ORDER_REFUND = "https://api.mch.weixin.qq.com/secapi/pay/refund"; // 申请退款

private static final String ORDER_REFUND_QUERY = "https://api.mch.weixin.qq.com/pay/refundquery"; // 申请退款

private static final String APP_ID = ConfigUtil.getProperty("wx.appid");

private static final String MCH_ID = ConfigUtil.getProperty("wx.mchid");

private static final String API_SECRET = ConfigUtil.getProperty("wx.api.secret");

/**

* 支付下订单

*

* @param request

* @param response

* @param cashnum

* 支付金额

* @param mercid

* 商品id

* @param callback

*/

@RequestMapping(value = "/pay", method = RequestMethod.POST)

public void orderPay(HttpServletRequest request, HttpServletResponse response,

@RequestParam(required = false, defaultValue = "0") Double cashnum, String mercid, String callback) {

LOG.info("[/order/pay]");

if (!"001".equals(mercid)) {

WebUtil.response(response, WebUtil.packJsonp(callback, JSON

.toJSONString(new JsonResult(-1, "商品不存在", new ResponseData()), SerializerFeatureUtil.FEATURES)));

}

Map restmap = null;

boolean flag = true; // 是否订单创建成功

try {

String total_fee = BigDecimal.valueOf(cashnum).multiply(BigDecimal.valueOf(100))

.setScale(0, BigDecimal.ROUND_HALF_UP).toString();

Map parm = new HashMap();

parm.put("appid", APP_ID);

parm.put("mch_id", MCH_ID);

parm.put("device_info", "WEB");

parm.put("nonce_str", PayUtil.getNonceStr());

parm.put("body", "测试付费");

parm.put("attach", "Andy");

parm.put("out_trade_no", PayUtil.getTradeNo());

parm.put("total_fee", total_fee);

parm.put("spbill_create_ip", PayUtil.getRemoteAddrIp(request));

parm.put("notify_url", "https://andy.org/wxpay/order/pay/notify.shtml");

parm.put("trade_type", "APP");

parm.put("sign", PayUtil.getSign(parm, API_SECRET));

String restxml = HttpUtils.post(ORDER_PAY, XmlUtil.xmlFormat(parm, false));

restmap = XmlUtil.xmlParse(restxml);

} catch (Exception e) {

LOG.error(e.getMessage(), e);

}

Map payMap = new HashMap();

if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code"))) {

payMap.put("appid", APP_ID);

payMap.put("partnerid", MCH_ID);

payMap.put("prepayid", restmap.get("prepay_id"));

payMap.put("package", "Sign=WXPay");

payMap.put("noncestr", PayUtil.getNonceStr());

payMap.put("timestamp", PayUtil.payTimestamp());

try {

payMap.put("sign", PayUtil.getSign(payMap, API_SECRET));

} catch (Exception e) {

flag = false;

}

}

if (flag) {

WebUtil.response(response,

WebUtil.packJsonp(callback,

JSON.toJSONString(new JsonResult(1, "订单获取成功", new ResponseData(null, payMap)),

SerializerFeatureUtil.FEATURES)));

} else {

if (CollectionUtil.isNotEmpty(restmap)) {

LOG.info("订单创建失败:" + restmap.get("err_code") + ":" + restmap.get("err_code_des"));

}

WebUtil.response(response, WebUtil.packJsonp(callback, JSON

.toJSONString(new JsonResult(-1, "订单获取失败", new ResponseData()), SerializerFeatureUtil.FEATURES)));

}

}

/**

* 查询支付结果

*

* @param request

* @param response

* @param tradeid 微信交易订单号

* @param tradeno 商品订单号

* @param callback

*/

@RequestMapping(value = "/pay/query", method = RequestMethod.POST)

public void orderPayQuery(HttpServletRequest request, HttpServletResponse response, String tradeid, String tradeno,

String callback) {

LOG.info("[/order/pay/query]");

if (StringUtil.isEmpty(tradeno) && StringUtil.isEmpty(tradeid)) {

WebUtil.response(response, WebUtil.packJsonp(callback, JSON

.toJSONString(new JsonResult(-1, "订单号不能为空", new ResponseData()), SerializerFeatureUtil.FEATURES)));

}

Map restmap = null;

try {

Map parm = new HashMap();

parm.put("appid", APP_ID);

parm.put("mch_id", MCH_ID);

parm.put("transaction_id", tradeid);

parm.put("out_trade_no", tradeno);

parm.put("nonce_str", PayUtil.getNonceStr());

parm.put("sign", PayUtil.getSign(parm, API_SECRET));

String restxml = HttpUtils.post(ORDER_PAY_QUERY, XmlUtil.xmlFormat(parm, false));

restmap = XmlUtil.xmlParse(restxml);

} catch (Exception e) {

LOG.error(e.getMessage(), e);

}

if (CollectionUtil.isNotEmptyhttp://(restmap) && "SUCCESS".equals(restmap.get("result_code"))) {

// 订单查询成功 处理业务逻辑

LOG.info("订单查询:订单" + restmap.yzWcfget("out_trade_no") + "支付成功");

WebUtil.response(response, WebUtil.packJsonp(callback, JSON

.toJSONString(new JsonResult(1, "订单支付成功", new ResponseData()), SerializerFeatureUtil.FEATURES)));

} else {

if (CollectionUtil.isNotEmpty(restmap)) {

LOG.info("订单支付失败:" + restmap.get("err_code") + ":" + restmap.get("err_code_des"));

}

WebUtil.response(response, WebUtil.packJsonp(callback, JSON

.toJSONString(new JsonResult(-1, "订单支付失败", new ResponseData()), SerializerFeatureUtil.FEATURES)));

}

}

/**

* 订单支付微信服务器异步通知

*

* @param request

* @param response

*/

@RequestMapping("/pay/notify")

public void orderPayNotify(HttpServletRequest request, HttpServletResponse response) {

LOG.info("[/order/pay/notify]");

response.setCharacterEncoding("UTF-8");

responhttp://se.setContentType("text/xml");

try {

ServletInputStream in = request.getInputStream();

String resxml = FileUtil.readInputStream2String(in);

Map restmap = XmlUtil.xmlParse(resxml);

LOG.info("支付结果通知:" + restmap);

if ("SUCCESS".equals(restmap.get("result_code"))) {

// 订单支付成功 业务处理

String out_trade_no = restmap.get("out_trade_no"); // 商户订单号

// 通过商户订单判断是否该订单已经处理 如果处理跳过 如果未处理先校验sign签名 再进行订单业务相关的处理

String sing = restmap.get("sign"); // 返回的签名

restmap.remove("sign");

String signnow = PayUtil.getSign(restmap, API_SECRET);

if (signnow.equals(sing)) {

// 进行业务处理

LOG.info("订单支付通知: 支付成功,订单号" + out_trade_no);

// 处理成功后相应给响应xml

Map respMap = new HashMap<>();

respMap = new HashMap();

respMap.put("return_code", "SUCCESS"); //相应给微信服务器

respMap.put("return_msg", "OK");

String resXml = XmlUtil.xmlFormat(restmap, true);

response.getWriter().write(resXml);

} else {

LOG.info("订单支付通知:签名错误");

}

} else {

LOG.info("订单支付通知:支付失败," + restmap.get("err_code") + ":" + restmap.get("err_code_des"));

}

} catch (Exception e) {

LOG.error(e.getMessage(), e);

}

}

/**

* 订单退款 需要双向证书验证

*

* @param request

* @param response

* @param tradeno 微信订单号

* @param orderno 商家订单号

* @param callback

*/

@RequestMapping(value = "/pay/refund", method = RequestMethod.POST)

public void orderPayRefund(HttpServletRequest request, HttpServletResponse response, String tradeno, String orderno,

String callback) {

LOG.info("[/pay/refund]");

if (StringUtil.isEmpty(tradeno) && StringUtil.isEmpty(orderno)) {

WebUtil.response(response, WebUtil.packJsonp(callback, JSON

.toJSONString(new JsonResult(-1, "订单号不能为空", new ResponseData()), SerializerFeatureUtil.FEATURES)));

}

Map restmap = null;

try {

Map parm = new HashMap();

parm.put("appid", APP_ID);

parm.put("mch_id", MCH_ID);

parm.put("nonce_str", PayUtil.getNonceStr());

parm.put("transaction_id", tradeno);

parm.put("out_trade_no", orderno);//订单号

parm.put("out_refund_no", PayUtil.getRefundNo()); //退款单号

parm.put("total_fee", "10"); // 订单总金额 从业务逻辑获取

parm.put("refund_fee", "10"); // 退款金额

parm.put("op_user_id", MCH_ID);

parm.put("refund_account", "REFUND_SOURCE_RECHARGE_FUNDS");//退款方式

parm.put("sign", PayUtil.getSign(parm, API_SECRET));

//String restxml = HttpUtils.posts(ORDER_REFUND, XmlUtil.xmlFormat(parm, false));

String restxml = HttpUtils.posts(ORDER_REFUND, XmlUtil.xmlFormat(parm, false));

restmap = XmlUtil.xmlParse(restxml);

} catch (Exception e) {

LOG.error(e.getMessage(), e);

}

Map refundMap = new HashMap<>();

if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code"))) {

refundMap.put("transaction_id", restmap.get("transaction_id"));

refundMap.put("out_trade_no", restmap.get("out_trade_no"));

refundMap.put("refund_id", restmap.get("refund_id"));

refundMap.put("out_refund_no", restmap.get("out_refund_no"));

LOG.info("订单退款:订单" + restmap.get("out_trade_no") + "退款成功,商户退款单号" + restmap.get("out_refund_no") + ",微信退款单号"

+ restmap.get("refund_id"));

WebUtil.response(response,

WebUtil.packJsonp(callback,

JSON.toJSONString(new JsonResult(1, "订单获取成功", new ResponseData(null, refundMap)),

SerializerFeatureUtil.FEATURES)));

} else {

if (CollectionUtil.isNotEmpty(restmap)) {

LOG.info("订单退款失败:" + restmap.get("err_code") + ":" + restmap.get("err_code_des"));

}

WebUtil.response(response, WebUtil.packJsonp(callback, JSON

.toJSONString(new JsonResult(-1, "订单退款失败", new ResponseData()), SerializerFeatureUtil.FEATURES)));

}

}

/**

* 订单退款查询

* @param request

* @param response

* @param tradeid 微信订单号

* @param tradeno 商户订单号

* @param refundid 微信退款号

* @param refundno 商家退款号

* @param callback

*/

@RequestMapping(value = "/pay/refund/query", method = RequestMethod.POST)

public void orderPayRefundQuery(HttpServletRequest request, HttpServletResponse response, String refundid,

String refundno, String tradeid, String tradeno, String callback) {

LOG.info("[/pay/refund/query]");

if (StringUtil.isEmpty(tradeid) && StringUtil.isEmpty(tradeno)

&& StringUtil.isEmpty(refundno) && StringUtil.isEmpty(refundid)) {

WebUtil.response(response, WebUtil.packJsonp(callback, JSON

.toJSONString(new JsonResult(-1, "退单号或订单号不能为空", new ResponseData()), SerializerFeatureUtil.FEATURES)));

}

Map restmap = null;

try {

Map parm = new HashMap();

parm.put("appid", APP_ID);

parm.put("mch_id", MCH_ID);

parm.put("transaction_id", tradeid);

parm.put("out_trade_no", tradeno);

parm.put("refund_id", refundid);

parm.put("out_refund_no", refundno);

parm.put("nonce_str", PayUtil.getNonceStr());

parm.put("sign", PayUtil.getSign(parm, API_SECRET));

String restxml = HttpUtils.post(ORDER_REFUND_QUERY, XmlUtil.xmlFormat(parm, false));

restmap = XmlUtil.xmlParse(restxml);

} catch (Exception e) {

LOG.error(e.getMessage(), e);

}

Map refundMap = new HashMap<>();

if (CollectionUtil.isNotEmpty(restmap) && "SUCCESS".equals(restmap.get("result_code")) && "SUCCESS".equals(restmap.get("result_code"))) {

// 订单退款查询成功 处理业务逻辑

LOG.info("退款订单查询:订单" + restmap.get("out_trade_no") + "退款成功,退款状态"+ restmap.get("refund_status_0"));

refundMap.put("transaction_id", restmap.get("transaction_id"));

refundMap.put("out_trade_no", restmap.get("out_trade_no"));

refundMap.put("refund_id", restmap.get("refund_id_0"));

refundMap.put("refund_no", restmap.get("out_refund_no_0"));

refundMap.put("refund_status", restmap.get("refund_status_0"));

WebUtil.response(response, WebUtil.packJsonp(callback, JSON

.toJSONString(new JsonResult(1, "订单退款成功", new ResponseData(null, refundMap)), SerializerFeatureUtil.FEATURES)));

} else {

if (CollectionUtil.isNotEmpty(restmap)) {

LOG.info("订单退款失败:" + restmap.get("err_code") + ":" + restmap.get("err_code_des"));

}

WebUtil.response(response, WebUtil.packJsonp(callback, JSON

.toJSONString(new JsonResult(-1, "订单退款失败", new ResponseData()), SerializerFeatureUtil.FEATURES)));

}

}

}

微信支付接口参数含义具体参考微信APP支付文档。

微信支付工具类

包含签名、订单号、退单号、随机串、服务器ip地址、客户端ip地址等方法。

package org.andy.wxpay.utils;

import java.io.UnsupportedEncodingException;

import java.net.URLEncoder;

import java.util.Arrays;

import java.util.Date;

import java.util.Map;

import java.util.Set;

import javax.servlet.http.HttpServletRequest;

/**

* 创建时间:2016年11月2日 下午7:12:44

*

* @author andy

* @version 2.2

*/

public class PayUtil {

/**

* 生成订单号

*

* @return

*/

public static String getTradeNo() {

// 自增8位数 00000001

return "TNO" + DatetimeUtil.formatDate(new Date(), DatetimeUtil.TIME_STAMP_PATTERN) + "00000001";

}

/**

* 退款单号

*

* @return

*/

public static String getRefundNo() {

// 自增8位数 00000001

return "RNO" + DatetimeUtil.formatDate(new Date(), DatetimeUtil.TIME_STAMP_PATTERN) + "00000001";

}

/**

* 退款单号

*

* @return

*/

public static String getTransferNo() {

// 自增8位数 00000001

return "TNO" + DatetimeUtil.formatDate(new Date(), DatetimeUtil.TIME_STAMP_PATTERN) + "00000001";

}

/**

* 返回客户端ip

*

* @param request

* @return

*/

public static String getRemoteAddrIp(HttpServletRequest request) {

String ip = request.getHeader("X-Forwarded-For");

if (StringUtil.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 (StringUtil.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)) {

return ip;

}

return request.getRemoteAddr();

}

/**

* 获取服务器的ip地址

*

* @param request

* @return

*/

public static String getLocalIp(HttpServletRequest request) {

return request.getLocalAddr();

}

public static String getSign(Map params, String paternerKey) throws UnsupportedEncodingException {

return MD5Utils.getMD5(createSign(params, false) + "&key=" + paternerKey).toUpperCase();

}

/**

* 构造签名

*

* @param params

* @param encode

* @return

* @throws UnsupportedEncodingException

*/

public static String createSign(Map params, boolean encode) throws UnsupportedEncodingException {

Set keysSet = params.keySet();

Object[] keys = keysSet.toArray();

Arrays.sort(keys);

StringBuffer temp = new StringBuffer();

boolean first = true;

for (Object key : keys) {

if (key == null || StringUtil.isEmpty(params.get(key))) // 参数为空不参与签名

continue;

if (first) {

first = false;

} else {

temp.append("&");

}

temp.append(key).append("=");

Object value = params.get(key);

String valueStr = "";

if (null != value) {

valueStr = value.toString();

}

if (encode) {

temp.append(URLEncoder.encode(valueStr, "UTF-8"));

} else {

temp.append(valueStr);

}

}

return temp.toString();

}

/**

* 创建支付随机字符串

* @return

*/

public static String getNonceStr(){

return RandomUtil.randomString(RandomUtil.LETTER_NUMBER_CHAR, 32);

}

/**

* 支付时间戳

* @return

*/

public static String payTimestamp() {

return Long.toString(System.currentTimeMillis() / 1000);

}

}

其他所需工具类参考项目源码

支付结果

APP支付测试完成

源代码地址:微信APP支付


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Maven项目改为spring boot项目的方法图解
下一篇:服装研发管理平台价格(服装研发中心)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~