java 微信退款接口_微信APP支付和退款(JAVA)

本文档详细介绍了如何使用Java实现微信APP支付及退款的过程,包括支付流程说明、Java demo实例、退款操作和转账操作。内容涵盖预支付订单实体对象、统一下单签名、XML格式转换、微信HTTP请求、预支付信息处理、微信回调处理、退款实体对象以及退款和转账的实现逻辑。
摘要由CSDN通过智能技术生成

微信APP支付和退款

微信支付流程说明

Java demo实例

退款

转账

1、微信支付

1 微信支付流程说明

本文支付流程说明均参考微信支付开发文档,微信支付开发文档,用于商户在移动端APP中集成微信支付功能。

商户APP调用微信提供的SDK调用微信支付模块,商户APP会跳转到微信中完成支付,支付完后跳回到商户APP内,最后展示支付结果。

下面看一下官方文档提供的业务流程图:

7ef0f25bacde

业务流程图

(ps:纵观所有的支付场景,包括博主接口过的支付方式比如Paypal(贝宝),Stripe(信用卡)等支付,他们的流程基本一致。)

概括起来就是:商户后端根据支付应用配置信息生成支付信息->App获取到支付信息->通过SDK发起支付请求->用户客户端输入密码->App 发起三方三方服务器支付请求->三方服务器回调商户后端服务器支付信息

Java demo实例

主要实现APP发起支付将支付金额传给后端服务器-后端服务器请求微信服务器生成预支付订单信息-返回APP端;

一、微信预支付订单实体对象

public class UnifiedorderResponse{

/**预付单信息**/

private String prepay_id;

/**签名**/

private String nonce_str;

/**微信开放平台申请的应用id**/

private String appid;

/**签名**/

private String sign;

/**请求方式**/

private String trade_type;

/**商户号id**/

private String mch_id;

/**返回提示信息**/

private String return_msg;

/**结果码**/

private String result_code;

/**返回码**/

private String return_code;

/**时间戳**/

private String timestamp;

}

二、微信统一下单实体对象

public class UnifiedorderModel {

/**微信开放平台申请的应用id**/

private String appid;

/**商户号*/

private String mch_id;

/**随机字符串*/

private String nonce_str;

/**签名(需要根据app应用商户密钥对整个统一下单实体对象就行签名)*/

private String sign;

/**签名方式*/

private String sign_type;

/**商品描述:腾讯充值中心-QQ会员充值*/

private String body;

/**商品详情**/

private String detail;

/**附加数据**/

private String attach;

/**支付订单号*/

private String out_trade_no;

/**总金额(分)*/

private Integer total_fee;

/**终端IP(8.8.8.8)*/

private String spbill_create_ip;

/**异步通知地址*/

private String notify_url;

/**交易类型**/

private String trade_type;

/**用户标识**/

private String openid;

/**商品id**/

private String product_id;

}

三、微信签名

对预支付请求实体属性进行字典排序,然后拼接字符串,用md5 加盐转大写生成签名字符串;

1、创建统一下单签名Map

/**

* 创建统一下单签名map

* @param request

* @param mchKey 商户密钥

* @return

*/

public static Map createUnifiedSign(UnifiedorderModel request) {

Map map = new HashMap<>();

map.put("appid", request.getAppid());

map.put("mch_id", request.getMch_id());

map.put("nonce_str", request.getNonce_str());

map.put("sign", request.getSign());

map.put("sign_type", request.getSign_type());

map.put("attach", request.getAttach());

map.put("body", request.getBody());

map.put("detail", request.getDetail());

map.put("notify_url", request.getNotify_url());

map.put("openid", request.getOpenid());

map.put("out_trade_no", request.getOut_trade_no());

map.put("spbill_create_ip", request.getSpbill_create_ip());

map.put("total_fee", String.valueOf(request.getTotal_fee()));

map.put("trade_type", request.getTrade_type());

return map;

}

2、按字典序排序微信统一签名

/**

* 微信统一签名

* @param params

* @return

*/

public static String sign(Map params,String mchKey) {

SortedMap sortedMap = new TreeMap<>(params);

StringBuilder toSign = new StringBuilder();

for (String key : sortedMap.keySet()) {

String value = params.get(key);

if (value!=null && !"".equals(value) && !"sign".equals(key)

&& !"key".equals(key)) {

toSign.append(key).append("=").append(value).append("&");

}

}

toSign.append("key=").append(mchKey);

return DigestUtils.md5Hex(toSign.toString()).toUpperCase();

}

四、XML格式转换

因为微信的请求和响应都是以xml的格式,所以需要将请求订单对象转换成XML 然后请求;

public class XMLUtil {

/**

* 解析字符串(XML)

*

* @param msg

* @return

* @throws Exception

*/

@SuppressWarnings("unchecked")

public static Map parseXml(String msg)

throws Exception {

// 将解析结果存储在HashMap中

Map map = new HashMap();

// 从request中取得输入流

InputStream inputStream = new ByteArrayInputStream(msg.getBytes("UTF-8"));

// 读取输入流

SAXReader reader = new SAXReader();

//防止微信支付XXE漏洞

reader.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);

Document document = reader.read(inputStream);

// 得到xml根元素

Element root = document.getRootElement();

// 得到根元素的所有子节点

List elementList = root.elements();

// 遍历所有子节点

for (Element e : elementList){

map.put(e.getName(), e.getText());

}

// 释放资源

inputStream.close();

inputStream = null;

return map;

}

/**

* 扩展xstream,使其支持CDATA块

*/

private static XStream xstream = new XStream(new XppDriver(new NoNameCoder()) {

@Override

public HierarchicalStreamWriter createWriter(Writer out) {

return new PrettyPrintWriter(out) {

// 对所有xml节点的转换都增加CDATA标记

boolean cdata = true;

@Override

@SuppressWarnings("rawtypes")

public void startNode(String name, Class clazz) {

super.startNode(name, clazz);

}

@Override

public String encodeNode(String name) {

return name;

}

@Override

protected void writeText(QuickWriter writer, String text) {

if (cdata) {

writer.write("

writer.write(text);

writer.write("]]>");

} else {

writer.write(text);

}

}

};

}

});

/**

* xml转对象

* @param xml

* @param objClass

* @return

*/

public static Object fromXML(String xml, Class> objClass) {

Serializer serializer = new Persister();

try {

return serializer.read(objClass, xml);

} catch (Exception e) {

e.printStackTrace();

}

return null;

}

/**

* 对象转xml

* @param obj

* @return

*/

public static String toXMl(Object obj) {

//使用注解设置别名必须在使用之前加上注解类才有作用

xstream.processAnnotations(obj.getClass());

return xstream.toXML(obj);

}

private XStream inclueUnderlineXstream = new XStream(new DomDriver(null,new XmlFriendlyNameCoder("_-", "_")));

public XStream getXstreamInclueUnderline() {

return inclueUnderlineXstream;

}

public XStream xstream() {

return xstream;

}

}

这里用到的XML POM 配置:

org.simpleframework

simple-xml

2.7.1

com.thoughtworks.xstream

xstream

1.4.3

五、微信HTTP请求

1、加载微信证书生成SSL请求(微信证书可以在微信商户后台下载)

//随机字符串范围

private static final String RANDOM_STR = "abcdefghijklmnopqrstuvwxyzABCDEFGHI"

+ "JKLMNOPQRSTUVWXYZ0123456789";

private static final java.util.Random RANDOM = new java.util.Random();

//支付证书认证

private static SSLContext sslContext;

/**

* 初始化微信证书

* @param req

* @return

* @throws MyException

*/

public static SSLContext initSSLContext(HttpServletRequest req,

String mchId)

throws MyException {

FileInputStream inputStream = null;

try {

inputStream = new FileInputStream(new File(req.getSession().getServletContext().

getRealPath("") + "/WEB-INF/classes"+BasicInfo.KeyPath));

} catch (IOException e) {

log.error("商户证书不正确-->>"+e);

throw new MyException("证书不正确!", e);

}

try {

KeyStore keystore = KeyStore.getInstance("PKCS12");

char[] partnerId2charArray = mchId.toCharArray();

keystore.load(inputStream, partnerId2charArray);

sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build();

return sslContext;

} catch (Exception e) {

log.error("商户秘钥不正确-->>"+e);

throw new MyException("秘钥不正确!", e);

} finally {

}

}

/**

* 生成随机字符串

* @return

*/

public static String getRandomStr() {

StringBuilder sb = new StringBuilder();

for (int i = 0; i < 16; i++) {

sb.append(RANDOM_STR.charAt(RANDOM.nextInt(RANDOM_STR.length())));

}

return sb.toString();

}

/**

* 发起微信支付相关请求

* @return

*/

public static String ssl(String url,String data,

HttpServletRequest req,String mchId){

StringBuffer message = new StringBuffer();

try {

// KeyStore keyStore = KeyStore.getInstance("PKCS12");

SSLContext sslcontext = initSSLContext(req,mchId);

SSLConnectionSocketFactory sslsf =

new SSLConnectionSocketFactory(sslcontext,

new String[] { "TLSv1" },

null,

SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

CloseableHttpClient httpclient = HttpClients.custom().

setSSLSocketFactory(sslsf).build();

HttpPost httpost = new HttpPost(url);

httpost.addHeader("Connection", "keep-alive");

httpost.addHeader("Accept", "*/*");

httpost.addHeader("Content-Type",

"application/x-www-form-urlencoded; charset=UTF-8");

httpost.addHeader("Host", "api.mch.weixin.qq.com");

httpost.addHeader("X-Requested-With", "XMLHttpRequest");

httpost.addHeader("Cache-Control", "max-age=0");

httpost.addHeader("User-Agent",

"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");

httpost.setEntity(new StringEntity(data, "UTF-8"));

CloseableHttpResponse response = httpclient.execute(httpost);

try {

HttpEntity entity = response.getEntity();

log.info("----------------------------------------");

log.info(response.getStatusLine());

if (entity != null) {

log.info("Response content length: " + entity.getContentLength());

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent(),"UTF-8"));

String text;

while ((text = bufferedReader.readLine()) != null) {

message.append(text);

}

}

EntityUtils.consume(entity);

} catch (IOException e) {

log.error("发起微信支付请求过程异常--》》"+e);

e.printStackTrace();

} finally {

response.close();

}

} catch (Exception e1) {

log.error("发起微信支付请求异常--》》"+e1);

e1.printStackTrace();

}

return message.toString();

}

六、发起预支付

/**

* APP微信支付

* @param req

* @param model

* @return

* @throws Exception

*/

public static UnifiedorderResponse payApp(HttpServletRequest req,

UnifiedorderModel model) throws Exception {

model.setNonce_str(PayUtil.getRandomStr());

model.setSign(SignUtil.sign(SignUtil.createUnifiedSign(model),

BasicInfo.APP_MchKey));

try{

XMLUtil xmlUtil= new XMLUtil();

xmlUtil.xstream().alias("xml", model.getClass());

String xml = xmlUtil.xstream().toXML(model);

String response = PayUtil.ssl(BasicInfo.unifiedordersurl, xml,

req,BasicInfo.APP_MchId);

UnifiedorderResponse ret = (UnifiedorderResponse)

XMLUtil.fromXML(response,UnifiedorderResponse.class);

System.out.println("-------------------");

System.out.println(JSONObject.toJSONString(ret));

if("SUCCESS".equals(ret.getResult_code())){

//再次签名

Map finalpackage = new TreeMap();

String timestamp = (System.currentTimeMillis()/1000)+"";

String noncestr = PayUtil.getRandomStr();

finalpackage.put("appid", BasicInfo.APP_AppID);

finalpackage.put("timestamp", timestamp);

finalpackage.put("noncestr", noncestr);

finalpackage.put("prepayid", ret.getPrepay_id());

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

finalpackage.put("partnerid",BasicInfo.APP_MchId);

String sign = SignUtil.sign(finalpackage, BasicInfo.APP_MchKey);

ret.setSign(sign);

ret.setNonce_str(noncestr);

ret.setTimestamp(timestamp);

return ret;

}else{

log.error("微信下单失败》》"+"错误码:"+ret.getReturn_code()+" ;"

+ "描述:"+ret.getReturn_msg());

return ret;

}

}catch (Exception e) {

log.error("微信下单异常》》"+e);

throw new MyException(SystemError.WX_PAY_ERROR.getCode(),

SystemError.WX_PAY_ERROR.getMessage());

}

}

预支付信息返回给前端;前端通过SDK发起支付请求;后端通过配置的回调地址接收微信的支付响应;

七、微信回调

@ResponseBody

@RequestMapping(value="/callBackWeiXinPay")

@Transactional(rollbackFor = Exception.class)

public void callBackWeiXinPay(HttpServletRequest req,

HttpServletResponse response) throws Exception{

try {

log.info("------------微信支付回调开始------------");

//支付回调

PayResponse pay = WxPay.callBackWeiXin(req);

//订单号

String orderNO = pay.getOut_trade_no();

log.info("微信支付回调订单号:"+orderNO);

//微信流水号

String thirdOrderNo = pay.getTransaction_id();

log.info("微信支付回调三方订单号:"+thirdOrderNo);

//支付金额

Integer money = pay.getTotal_fee();

log.info("微信支付回调金额(分):"+money);

//todo 业务处理比如更新订单状态等

//响应微信

CallBackWxModel call = new CallBackWxModel();

call.setReturn_code("SUCCESS");

call.setReturn_msg("");

// 响应xml格式数据

String resXml = XMLUtil.toXMl(call);

response.getWriter().write(resXml);

log.info("------------微信支付回调结束------------");

} catch (Exception e) {

log.error("微信支付回调异常CallBackController-->>callBackPay:\r\n"+ e.getMessage());

log.error("微信支付回调异常CallBackController-->>callBackPay:\r\n", e);

}

}

2、退款

一、退款实体对象

public class RefundModel {

/**Appid*/

private String appid;

/**商户号*/

private String mch_id;

/**随机字符串*/

private String nonce_str;

/**签名*/

private String sign;

/**签名方式*/

private String sign_type;

/**支付订单号*/

private String out_trade_no;

/**退款订单号*/

private String out_refund_no;

/**总金额*/

private Integer total_fee;

/**退款金额*/

private Integer refund_fee;

/**退款原因*/

private String refund_desc;

}

二、退款

/**

* 微信退款

* @param rdfund

* @param req

* @param type 1-公众号 2-小程序 3-app

* @return

* @throws Exception

*/

public static Map refundUtil(RefundModel rdfund

, HttpServletRequest req,Integer type) throws Exception{

Map mp = new HashMap();

//设置支付账户信息

String MchKey = ""; //商户号秘钥

String MchId = ""; //商户号id

rdfund.setAppid(BasicInfo.APP_AppID);

rdfund.setMch_id(BasicInfo.APP_MchId);

MchKey = BasicInfo.APP_MchKey;

MchId = BasicInfo.APP_MchId;

rdfund.setNonce_str(PayUtil.getRandomStr());

rdfund.setSign_type("MD5");

rdfund.setSign(SignUtil.sign(SignUtil.createRefundSign(rdfund),MchKey));

try{

XMLUtil xmlUtil= new XMLUtil();

xmlUtil.xstream().alias("xml", rdfund.getClass());

String xml = xmlUtil.xstream().toXML(rdfund);

String response = PayUtil.ssl(BasicInfo.refundurl, xml,req,MchId);

System.out.println(response);

Map map = xmlUtil.parseXml(response);

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

mp.put("stu", true);

return mp;

}else{

mp.put("stu", false);

mp.put("errMsg", map.get("return_msg"));

mp.put("errDes", map.get("err_code_des"));

log.error("微信退款失败》》"+"错误码:"+map.get("return_msg")+" ;"

+ "描述:"+map.get("err_code_des"));

return mp;

}

}catch (Exception e) {

log.error("微信退款异常》》"+e);

throw new MyException(SystemError.WX_RETREIEV_ERROR.getCode(),

SystemError.WX_RETREIEV_ERROR.getMessage());

}

}

主要逻辑跟支付差不多,可以参考前面的demo;

3、转账

一、转账实体对象

/***appId****/

private String mch_appid;

/***商户号****/

private String mchid;

/***随机字符串****/

private String nonce_str;

/***签名****/

private String sign;

/***商户订单号****/

private String partner_trade_no;

/***用户openid****/

private String openid;

/****校验用户姓名选项

* NO_CHECK:不校验真实姓名

FORCE_CHECK:强校验真实姓名***/

private String check_name;

/***收款用户姓名****/

private String re_user_name;

/****金额(分)***/

private Integer amount;

/***企业付款描述信息****/

private String desc;

/***Ip地址****/

private String spbill_create_ip;

二、转账

/**

* 企业转账接口

* @param transfer

* @param req

* @return

* @throws Exception

*/

public static Map transfersUtil(TransfersModel transfer

, HttpServletRequest req) throws Exception{

Map mp = new HashMap();

transfer.setMch_appid(BasicInfo.appID);

transfer.setMchid(BasicInfo.MchId);

transfer.setNonce_str(PayUtil.getRandomStr());

transfer.setCheck_name("NO_CHECK");

transfer.setSpbill_create_ip("127.0.0.1");

transfer.setSign(SignUtil.sign(SignUtil.createtransfersSign(transfer),BasicInfo.MchKey));

try{

XMLUtil xmlUtil= new XMLUtil();

xmlUtil.xstream().alias("xml", transfer.getClass());

String xml = xmlUtil.xstream().toXML(transfer);

String response = PayUtil.ssl(BasicInfo.transfersurl, xml,req,BasicInfo.MchKey);

System.out.println(response);

Map map = xmlUtil.parseXml(response);

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

mp.put("stu", true);

return mp;

}else{

mp.put("stu", false);

mp.put("errMsg", map.get("return_msg"));

mp.put("errDes", map.get("err_code_des"));

log.error("企业转账失败》》"+"错误码:"+map.get("return_msg")+" ;"

+ "描述:"+map.get("err_code_des"));

return mp;

}

}catch (Exception e) {

log.error("企业转账异常》》"+e);

throw new MyException(SystemError.WX_RETREIEV_ERROR.getCode(),

SystemError.WX_RETREIEV_ERROR.getMessage());

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值