当前位置: 移动技术网 > IT编程>开发语言>Java > java实现微信App支付服务端

java实现微信App支付服务端

2019年07月19日  | 移动技术网IT编程  | 我要评论
微信app支付服务端的实现方法,供大家参考,具体内容如下 引言 主要实现app支付统一下单、异步通知、调起支付接口、支付订单查询、申请退款、查询退款功能;封装了http

微信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<string, string> restmap = null;
 boolean flag = true; // 是否订单创建成功
 try {
 string total_fee = bigdecimal.valueof(cashnum).multiply(bigdecimal.valueof(100))
 .setscale(0, bigdecimal.round_half_up).tostring();
 map<string, string> parm = new hashmap<string, string>();
 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://www.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<string, string> paymap = new hashmap<string, string>();
 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<string, string> restmap = null;
 try {
 map<string, string> parm = new hashmap<string, string>();
 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.isnotempty(restmap) && "success".equals(restmap.get("result_code"))) {
 // 订单查询成功 处理业务逻辑
 log.info("订单查询:订单" + restmap.get("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");
 response.setcontenttype("text/xml");
 try {
 servletinputstream in = request.getinputstream();
 string resxml = fileutil.readinputstream2string(in);
 map<string, string> 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<string, string> respmap = new hashmap<>();
 respmap = new hashmap<string, string>(); 
 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<string, string> restmap = null;
 try {
 map<string, string> parm = new hashmap<string, string>();
 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<string, string> 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<string, string> restmap = null;
 try {
 map<string, string> parm = new hashmap<string, string>();
 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<string, string> 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<string, string> 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<string, string> params, boolean encode) throws unsupportedencodingexception {
 set<string> 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支付测试完成

源代码地址:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网