场景
微信支付需要配置回调地址。当用户发起微信支付,点击付款成功后,微信会调用应用程序配置好的URL直接,意思是说支付成功了。在这个回调成功的方法里面,应用程序可以处理自己的业务逻辑。如修改订单状态,减少库存等。
存在问题
开发环境,IP多为内网IP,不是公网IP,这样微信回调是回调不回来的。当然也可以通过反向代理ngrok结合nginx来搞。
这里主要是通过程序,用redis句柄来模拟各个环境的微信支付回调。
核心代码
CallBakController
package com.weixin.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.weixin.bean.CallBack;
import com.weixin.config.WeixinConfig;
import com.weixin.redis.WeixinCallBackRedisDbManager;
import com.weixin.util.OrderUtils;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Map;
/**
* Created by wangshuang on 17/4/6.
*/
@Controller
@RequestMapping("/weixin")
public class CallBakController {
@Autowired
private WeixinCallBackRedisDbManager weixinCallBackRedisDbManager;
@RequestMapping(value = "/pay/callback", method = RequestMethod.POST)
@ResponseBody
public JSONObject getTeachersByCategory(@RequestParam(required = true) String orderNo) {
JSONObject returnObj = new JSONObject();
CallBack callBack = new CallBack();
if(StringUtils.isEmpty(orderNo)){
returnObj.put("status", "10001");
returnObj.put("msg", "订单编号不能为空");
return returnObj;
}
int overTime = 30; // 设置过期时间
List<NameValuePair> packageParams = null;
try {
// 获取微信回调信息
if(weixinCallBackRedisDbManager.checkCallBackKey(orderNo)){
callBack = weixinCallBackRedisDbManager.getCallBackInfo(orderNo);
}
if(null == callBack){
returnObj.put("status", "10001");
returnObj.put("msg", "未匹配订单编号");
return returnObj;
}
packageParams = OrderUtils.CallBakInfo(
callBack.getOut_trade_no(),
callBack.getTotal_fee(),
callBack.getTrade_type(),
callBack.getOpenid(),
callBack.getAppid(),
callBack.getMch_id(),
overTime,
callBack.getTransaction_id());
String sign = OrderUtils.genWeixinSign(packageParams, WeixinConfig.GZH_KEY);
packageParams.add(new NameValuePair("sign", sign));
String params = OrderUtils.toXml(packageParams);
Map<String, String> paramsMap = OrderUtils.sendWeixinPost(params);
returnObj = (JSONObject) JSON.toJSON(paramsMap);
return returnObj;
} catch (UnknownHostException e) {
returnObj.put("status", "500");
returnObj.put("msg", "访问无效");
return returnObj;
} catch (Exception e) {
returnObj.put("status", "500");
returnObj.put("msg", "模拟微信支付回调失败");
return returnObj;
}
}
}
CallBack回调返回参数封装
package com.weixin.bean;
/**
* Created by wangshuang on 17/4/7.
*/
public class CallBack {
//公众号ID
private String appid;
//付款银行
private String bank_type;
//现金支付金额
private String cash_fee;
//货币种类
private String fee_type;
//是否关注公众账号
private String is_subscribe;
//商户号
private String mch_id;
//随机字符串
private String nonce_str;
//openid
private String openid;
//商户订单号
private String out_trade_no;
//业务结果
private String result_code;
//返回状态码
private String return_code;
//支付完成时间
private String time_end;
//总金额
private String total_fee;
//交易类型
private String trade_type;
//微信支付订单号
private String transaction_id;
//签名
private String sign;
此处get/set方法省略。。。
}
发送POST请求给应用程序
/**
* 发送微信请求 sendWeixinPost:
*
* @author sid
* @param xml
* @return
*/
public static Map<String, String> sendWeixinPost(String xml) {
PostMethod post = new PostMethod(WeixinConfig.USERVICE_URL);
post.setRequestHeader("Pragma:", "no-cache");
post.setRequestHeader("Cache-Control", "no-cache");
post.setRequestHeader("Content-Type", "text/xml");
post.setRequestBody(xml);
try {
String res = HttpClientUtil.httpRequest(post);
// 获取微信POST过来反馈信息
Map<String, String> params = XmlConverUtil.xmltoMap(res);
return params;
} catch (ConnectTimeoutException e) {
e.printStackTrace();
} catch (HttpException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
redis 具体操作类
package com.weixin.redis;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.weixin.bean.CallBack;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
/**
* Created by wangshuang on 17/4/7.
*/
@Repository
public class WeixinCallBackRedisDbManager {
@Autowired
JedisPool jedisPool;
/**
* 用户信息过期时间:半小时
*/
private static int EXPIRE_TIME = 60 * 30;
// 微信回调key
private static final String WEI_XIN_CALL_BACK = "WEI_XIN_CALL_BACK" + ":";
// 保存微信回调数据
public void saveCallBackInfo(CallBack callBack, String orderNo) {
Jedis jedis = jedisPool.getResource();
String key = WEI_XIN_CALL_BACK + orderNo;
String weixinInfo = JSONObject.toJSONString(callBack);
jedis.set(key, weixinInfo);
jedis.expire(key, EXPIRE_TIME);
jedis.close();
}
// 判断微信回调key是否存在
public boolean checkCallBackKey(String orderNo){
Jedis jedis = jedisPool.getResource();
String key = WEI_XIN_CALL_BACK + orderNo;
boolean flag = jedis.exists(key);
jedis.close();
return flag;
}
// 获取微信回调数据
public CallBack getCallBackInfo(String orderNo){
Jedis jedis = jedisPool.getResource();
String key = WEI_XIN_CALL_BACK + orderNo;
CallBack callback = (CallBack) JSON.parseObject(jedis.get(key), CallBack.class);
jedis.close();
return callback;
}
}
读取配置文件工具类
package com.weixin.util;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* Created by wangshuang on 17/4/6.
*/
public class ConfigUtil {
static Properties properties=new Properties();
private static void load(){
InputStream is= ConfigUtil.class.getResourceAsStream("/application.properties");
try {
properties.load(is);
} catch (IOException e) {
e.printStackTrace();
}
}
public static String getProperty(String key){
String value= properties.getProperty(key);
if(value==null){
load();
value= properties.getProperty(key);
}
return value;
}
public static int getIntProperty(String key){
String value=getProperty(key);
if(value==null)return 0;
return Integer.parseInt(value.trim());
}
public static void main(String[] args) {
System.out.println(ConfigUtil.getProperty("uServiceUrl"));
}
}
说明:
- 项目工程代码已经全部开源,可以下载下来直接运行,保密原则,微信相关的appId和macId已经全部注释掉。只需要替换成自己微信公众号的相应值即可。
- uServiceUrl需要配置成自己应用的回调地址
开源地址:https://github.com/wangxiaoda513/weixinCallBak
谢谢大家,互相学习,互相进步。