微信支付实战(完整的代码,复制即可用)

公众号介绍

https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html
最重要的一点:
公众平台以access_token为接口调用凭据,来调用接口,所有接口的调用需要先获取access_token,access_token在2小时内有效,过期需要重新获取,但1天内获取次数有限,开发者需自行存储,详见获取接口调用凭据(access_token)文档。

公众号配置

开通<网页授权获取用户基本信息>接口

在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的“开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名。请注意,这里填写的是域名(是一个字符串),而不是URL,因此请勿加 http:// 等协议头;。

授权回调域名配置规范为全域名,比如需要网页授权的域名为:www.qq.com,配置以后此域名下面的页面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以进行OAuth2.0鉴权。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com 无法进行OAuth2.0鉴权

开通<获取access_token>接口

开通<获取jsapi_ticket>接口

以上三个是最核心的接口必须开通,其他接口根据需要开通

配置<JS接口安全域名>

先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。

备注:登录后可在“开发者中心”查看对应的接口权限。

配置ip白名单

登录微信公众平台–>开发>基本配置>ip白名单

提前将服务器 IP 地址添加到 IP 白名单中,否则将无法调用成功。小程序无需配置 IP 白名单。

微信支付产品介绍

(1)付款码支付
付款码-线下支付,商户展示一个有金额二维码,用户扫描二维码并输入密码进行支付。

(2)JSAPI支付
JSAPI-公众号支付,商户通过公众号调起微信支付页面,支付页面已固定金额,用户输入密码进行支付。
JSAPI-线下支付,商户展示一个无金额二维码,用户扫描二维码并输入金额和密码进行支付。
JSAPI-PC网站支付,商户展示一个无金额二维码,用户扫描二维码并输入金额和密码进行支付。

(3)小程序支付
小程序支付,商户通过小程序调起微信支付页面,支付页面已固定金额,用户输入密码进行支付。

(4)Native支付
Native-线下支付,商户展示一个有金额二维码,用户扫描二维码并输入密码进行支付。
Native-PC网站支付,商户展示一个有金额二维码,用户扫描二维码并输入密码进行支付。

(5)APP支付
在商户自己开发的手机App(Android、IOS)上调起微信支付页面,支付页面已固定金额,用户输入密码进行支付。

(6)刷脸支付
不清楚,如果需要使用自己去查询官方文档。

以上支付只是客户端的不用应用场景,但对后台开发人员来说都一样,因为微信有统一的支付接口:https://api.mch.weixin.qq.com/pay/unifiedorder
一般来说我们选JSAP支付就行了,
JSAP支付和Native支付的区别就是JSAPI展示的是无金额二维码,Native展示的是有金额二维码。

微信支付接入流程

(1)获取商户号
提交资料=>签署协议=>获取商户号

(2)获取 APPID
步骤:注册服务号=>服务号认证=>获取APPID=>绑定商户号

(3)获取API秘钥
登录商户平台=>选择账户中心=>安全中心=>API安全=>设置API密钥

(4)获取APlv3秘钥
登录商户平台=>选择账户中心=>安全中心=>API安全=>设置APIV3密钥

(5)申请商户API证书
APIv3版本的所有接口都需要;APIv2版本的高级接口需要(如:退款、企业红包、企业付款等)
登录商户平台=>选择账户中心=>安全中心=>API安全=>申请API证书

下载证书工具=>解压(下载的exe其实是个解压工具)证书工具=>运行证书工具=>选择证书保存路径=>点击申请证书=>填写商户号和商户名称=>下一步=>复制请求字符串=>回到商户平台=>粘贴请求字符串=>输入商户平台密码=>复制证书字符串=>回到证书工具=>下一步=>粘贴证书字符串=>下一步=>查看证书文件夹=>解压zip=>完工。

解压后apiclient_cert.p12、apiclient_cert.pem、apiclient_key.pem就是我们的商户API证书,其中apiclient_cert.p12是后台开发调用退款接口需要的文件,其他两个文件我没发现有地方需要使用它们。

附三个文件的介绍:
证书pkcs12格式(apiclient_cert.p12):包含了私钥信息的证书文件,为p12(pfx)格式,由微信支付签发给您用来标识和界定您的身份。部分安全性要求较高的API需要使用该证书来确认您的调用身份。
证书pem格式(apiclient_cert.pem):从apiclient_cert.p12中导出证书部分的文件,为pem格式,证书序列号也可以从这个文件里解析得到
证书密钥pem格式(apiclient_key.pem):从apiclient_cert.p12中导出密钥部分的文件。

(6)获取微信平台证书
可以预先下载,也可以通过编程的方式获取。

商户号配置

配置支付目录

支付授权目录说明:
1、商户最后请求拉起微信支付收银台的页面地址我们称之为“支付目录”,例如:https://www.weixin.com/pay.php。
2、商户实际的支付目录必须和在微信支付商户平台设置的一致,否则会报错“当前页面的URL未注册:”

支付授权目录设置说明:
登录微信支付商户平台(pay.weixin.qq.com)–>产品中心–>开发配置,设置后一般5分钟内生效。

支付授权目录校验规则说明:
1、如果支付授权目录设置为顶级域名(例如:https://www.weixin.com/ ),那么只校验顶级域名,不校验后缀;
2、如果支付授权目录设置为多级目录,就会进行全匹配,例如设置支付授权目录为https://www.weixin.com/abc/123/,则实际请求页面目录不能为https://www.weixin.com/abc/,也不能为https://www.weixin.com/abc/123/pay/,必须为https://www.weixin.com/abc/123/

开发介绍

纯手写,没有使用微信提供的JavaSDK(因为也没几句代码)。
使用JSAPI-公众号支付支付方式进行开发。
项目技术选型:
非前后端分离的传统SSM架构,非微服务,非分布式,单服务器部署。

支付流程

假设我们的公众号已经开发好了,上面有个商品,用户点击购买进行下单,OK,这是前端支付事件的最源头。
从这个最源头出发,实际上的流程是:

用户点击下单=>调用后台系统生成订单=>后台调用微信统一支付接口并获取prepay_id=>返回公众号页面,使用JSAPI调起微信支付页面,并传入prepay_id=>用户输入支付密码完成支付=>微信支付成功回调后台系统=>后台系统修改订单的支付状态。

OpenID

OpenID是微信公众号中为了鉴别用户而设计的唯一标识,每个用户针对每个公众号会产生一个安全的OpenID。

我们后台如果要设计一个微信用户表用来储存微信用户信息,那这个表和我们系统原来的用户表一定是多对一的,因为我们可能会有多个公众号,这样一来一个用户就有多个OpenID。

一般来说,在用户第一次访问公众号时(即从公众号菜单点进来的)获取OpenID,获取一次后返回给公众号前端页面,前端每次调用后台接口时,都传入这个OpenID,后台接口就知道是哪个用户在操作了,相当于PC网站的Session。

OpenID一旦拿到手,需要在页面上和后台之间反复传输已避免丢失(因为你不保存在前端页面,就需要后台接口每次都需要调用微信以获取OpenID,很浪费时间的,而且OpenID又不会变化)。

不想传来传去的话,可以将OpenID存到session中,也行,但是session一失效又得重新获取,而且重新获取时重定向不知道原页面了,只能重定向到首页。只能说和页面传值的方式对比,各有利弊。
以前的微信公众号不能使用session,现在的微信公众号是可以使用session的,和pc浏览器的session无异。

看这里,很重要:
微信获取OpenID需要通过重定向https://open.weixin.qq.com/connect/oauth2/authorize这个链接,然后再定义一个接口接收微信的回调请求,然后在回调接口里再去调用https://api.weixin.qq.com/sns/oauth2/access_token接口获取OpenID。

获取OpenID的微信接口1:

定义goods/menu接口(公众号菜单配的链接对应的后台接口):

@Controller
@RequestMapping("/goods")
public class GoodsController extends WechatController {
	// 用户通过微信内置浏览器访问此接口
	// 如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息(目前我们只需要openid),进而实现业务逻辑。
	@RequestMapping(value = "/menu", method = RequestMethod.GET)
	public String menu(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (Utils.isEmptyTrim(request.getSession().getAttribute("openId"))) {//如果没有获取openId,那么去获取完openId再来
			String redirectUrl="http://www.xuexibisai.com/wechat/code/1";
			WechatUtil.redirectGetOpenId(request, response, redirectUrl);
			return null;
		}else {
			return "forward:/goods/list.do";//转发至goods/list.do
		}
	}
}
public class WechatUtil {
		public static final String redirect_openId_url = "https://open.weixin.qq.com/connect/oauth2/authorize?";
	// 重定向获取openid
	public static void redirectGetOpenId(HttpServletRequest request, HttpServletResponse response, String url) {
		try {
			LOG.debug("微信认证后待访问url:" + url);
			// scope参数只有两种应用授权作用域,snsapi_base
			// (不弹出授权页面,直接跳转,只能获取用户openid),snsapi_userinfo
			// (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且, 即使在未关注的情况下,只要用户授权,也能获取其信息 )
			// state参数是开发者自定义的参数,重定向后会带上state参数,开发者可以填写a-zA-Z0-9的参数值,最多128字节
			response.sendRedirect(redirect_openId_url +"appid=" + WechatUtil.appId + "&redirect_uri=" + URLEncoder.encode(url, "utf-8")
					+ "&response_type=code&scope=snsapi_base&state=1#wechat_redirect");
			// 如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
		} catch (Exception e) {
			LOG.error("异常", e);
		}
	}
}

获取OpenID的微信接口2:

定义wechat/code接口:

@Controller
@RequestMapping("/wechat")
public class WechatController{
	private static final Logger LOG = LoggerFactory.getLogger(WechatController.class);
	
	@Autowired
	private WxUserInfoMapper wxUserInfoMapper;
	
	// 经过微信重定向后,会返回code,然后根据code获取openid,并将openid返回给页面
	@RequestMapping(value = "/code/{menuType}", method = RequestMethod.GET)
	public ModelAndView code(HttpServletRequest request, HttpServletResponse response, String code,@PathVariable(value="menuType") Integer menuType) throws Exception {
		//通过code查openid
		String openId = WechatUtil.getOpenId(code);
		if (Utils.isEmptyTrim(openId)) {
			return WebUtil.getErrorPage("获取openId失败", LOG, "");
		}
		// 通过openid查用户信息
		JSONObject jsonObject = WechatUtil.getUserInfo(openId,0);
		LOG.debug("最新的微信用户信息:" + jsonObject.toJSONString());
		if (Utils.isEmpty(jsonObject, "openid")) {
			return WebUtil.getErrorPage("查询微信用户信息失败", LOG, "");
		}
		// 查数据库用户信息
		WxUserInfo wxUserInfo = wxUserInfoMapper.selectByOpenId(openId);
		if (!Utils.isEmpty(jsonObject, "openid")) {
			if (wxUserInfo == null) {// 如果数据库没有就插入用户信息
				wxUserInfo = new WxUserInfo();
				wxUserInfo.setAppId(WechatUtil.appId);
				wxUserInfo.setOpenid(openId);
				wxUserInfo.setCreateTime(new Date());
			}
			wxUserInfo.setNickname(jsonObject.getString("nickname"));
			wxUserInfo.setHeadimgurl(jsonObject.getString("headimgurl"));
			wxUserInfo.setSex(jsonObject.getInteger("sex"));
			wxUserInfo.setCity(jsonObject.getString("city"));
			wxUserInfo.setProvince(jsonObject.getString("province"));
			wxUserInfo.setCountry(jsonObject.getString("country"));
			wxUserInfo.setSubscribe(jsonObject.getInteger("subscribe"));
			wxUserInfo.setSubscribeTime(jsonObject.getLong("subscribe_time"));
			wxUserInfo.setGroupid(jsonObject.getInteger("groupid"));
			wxUserInfo.setRemark(jsonObject.getString("remark"));
			if (!Utils.isEmpty(jsonObject.getString("unionid"))) {
				wxUserInfo.setUnionid(jsonObject.getString("unionid"));
			}
			wxUserInfo.setUpdateTime(new Date());

			if (Utils.isEmpty(wxUserInfo.getUserId())) {
				wxUserInfoMapper.insertSelective(wxUserInfo);
				LOG.debug("添加微信用户信息成功");
			} else {
				wxUserInfoMapper.updateByPrimaryKeySelective(wxUserInfo);
				LOG.debug("更新微信用户信息成功");
			}
			request.getSession().setAttribute("openId", openId);
			if (menuType==0) {
				return new ModelAndView("forward:goods_list");//跳转至商品首页
			}else if(menuType==1) {
				return new ModelAndView("forward:user_list");
			}
		}
		return WebUtil.getErrorPage("查询微信用户信息失败", LOG, "");
	}
}
public class WechatUtil {
		public static final String get_openId_url = "https://api.weixin.qq.com/sns/oauth2/access_token?";
	/**
	 * 通过授权令牌获取用户openId
	 */
	public static String getOpenId(String code) {
		String openid = null;
		StringBuilder url = new StringBuilder();
		url.append(get_openId_url);
		url.append("&appid=" + appId);
		url.append("&secret=" + appSecret);
		url.append("&code=").append(code);
		url.append("&grant_type=authorization_code");
		log.debug("url=" + url.toString());
		String ret = sendDataHttpsViaGet(url.toString());
		log.debug("获取openId" + ret);
		JSONObject obj = JSONObject.parseObject(ret);
		String errcode = obj.getString("errcode");
		if (StringUtils.isBlank(errcode)) {
			openid = obj.getString("openid");
		}
		return openid;
	}
}

假如用户

微信的接口鉴权机制

获取openId其实只是获取用户相关信息,我们要想调用后续的其他接口,还必须获取,access_token和jsapi_ticket。

获取jsapi_ticket之前需要获取access_token,access_token是微信公众号开发所有后台接口都需要传入的参数。

jsapi_ticket

jsapi_ticket是前端js接口声明(wx.config)时所需的信息要用到。

$(function() {
	$.ajax({
		url : '${ctx}/goods/sign.do',
		async : false,
		data : {
			'url' : window.location.href
		},
		dataType : 'json',
		type : 'post',
		success : function(result) {
			var data = result.data;
			if (data.appid == null || data.appid == "") {
				return;
			}
			wx.config({
				debug : false,
				appId : data.appid,
				timestamp : data.timestamp,
				nonceStr : data.nonceStr,
				signature : data.signature,//这个signature就是jsapi_ticket通过加密解密转化来的。
				jsApiList : [ "chooseWXPay" ]
			});
		}
	});
	wx.error(function(res) {
		alert("页面鉴权失败:"+res);
	});
	wx.ready(function() {
	});
});
@Controller
@RequestMapping("/goods")
public class GoodsController extends WechatController {
	private static final Logger LOG = LoggerFactory.getLogger(GoodsController.class);
	@RequestMapping(value = "/sign.do")
	@ResponseBody
	public Map<String, Object> sign(HttpServletRequest request, HttpServletResponse response, String url) {
		if (StringUtils.isBlank(url)) {
			LOG.info("url传递失败");
			return PortalUtil.fail("传递失败");
		}
		Map<String, String> ret = WechatUtil.sign(url);
		LOG.debug("ret:=" + ret);
		ret.put("appid", WechatUtil.appId);
		return PortalUtil.success(ret);
	}
}
public class WechatUtil {
	public static Map<String, String> sign(String url) {
		Map<String, String> ret = new HashMap<String, String>();
		String nonceStr = createNonceStr();
		String timestamp = createTimestamp();
		String string1;
		String signature = "";
		// 注意这里参数名必须全部小写,且必须有序
		string1 = "jsapi_ticket=" + getJsapiTicket() + "&noncestr=" + nonceStr + "&timestamp=" + timestamp + "&url=" + url;
		try {
			MessageDigest crypt = MessageDigest.getInstance("SHA-1");
			crypt.reset();
			crypt.update(string1.getBytes("UTF-8"));
			signature = byteToHex(crypt.digest());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}

		ret.put("url", url);
		ret.put("nonceStr", nonceStr);
		ret.put("timestamp", timestamp);
		ret.put("signature", signature);
		return ret;
	}
	
	public static final String get_jsapi_ticket_url="https://api.weixin.qq.com/cgi-bin/ticket/getticket?";
	private static String jsapiTicket;
	private static long jsapiTicketTime;// 获取凭证时的时间,单位:毫秒数,System.currentTimeMillis
	private static int jsapiTicketExpireTime;// 凭证有效时间,单位:秒

	private static void clearJsapiTicket() {
		jsapiTicket = "";
		jsapiTicketExpireTime = 0;
		jsapiTicketTime=0;
	}
	
	public static String getJsapiTicket() {
		if (!Utils.isEmptyTrim(jsapiTicket) && jsapiTicketTime > 0 && jsapiTicketExpireTime > 0) {
			long extime = jsapiTicketTime + (jsapiTicketExpireTime * 1000);
			long nowTime = System.currentTimeMillis();
			if (extime - nowTime > 1000) {// 仍然有效
				return jsapiTicket;
			}
		}
		try {
			String url = get_jsapi_ticket_url+"access_token=" + getAccessToken()
					+ "&type=jsapi";
			String response = sendDataHttpsViaGet(url);
			JSONObject json = JSONObject.parseObject(response);
			log.info(json.toJSONString());
			String errmsg=json.getString("errmsg");
			String errcode=json.getString("errcode");
			if(!errmsg.equals("ok")&&!errcode.equals("0")){
				return null;
			}
			log.debug("获取到新的JsapiTicket:"+json.toJSONString());
			jsapiTicketExpireTime = json.getIntValue("expires_in");
			jsapiTicket = json.getString("ticket");
			jsapiTicketTime=System.currentTimeMillis();
			
		} catch (Exception e) {
			LOG.error("getjsapiTicket-error", e);
			clearJsapiTicket();
		}
		LOG.debug("jsapiTicket===" + jsapiTicket);
		System.out.println("jsapiTicket===" + jsapiTicket);
		return jsapiTicket;
	}
}

access_token

要想获取jsapi_ticket,就要先获取access_token。
不要频繁去调用微信接口获取access_token,每天调用获取access_token接口的次数微信有限制,只需要判断过期时再去获取access_token。

public class WechatUtil {
	public static final String get_access_token_url="https://api.weixin.qq.com/cgi-bin/token?";
	private static String accessToken;
	private static long accessTokenTime;// 获取凭证时的时间,单位:毫秒数,System.currentTimeMillis
	private static int accessTokenExpireTime;// 凭证有效时间,单位:秒

	private static void clearAccessToken() {
		accessToken = "";
		accessTokenExpireTime = 0;
		accessTokenTime=0;
	}

	public static String getAccessToken() {
		if (!Utils.isEmptyTrim(accessToken) && accessTokenTime > 0 && accessTokenExpireTime > 0) {
			long extime = accessTokenTime + (accessTokenExpireTime * 1000);
			long nowTime = System.currentTimeMillis();
			if (extime - nowTime > 1000) {// 仍然有效
				return accessToken;
			}
		}
		try {
			String url = get_access_token_url + "&grant_type=client_credential&appid=" + appId + "&secret="
					+ appSecret;
			log.debug("进来url:="+url);
			String response = sendDataHttpsViaGet(url);
			if(StringUtils.isBlank(response)){
				log.debug("response为空:="+response);
				clearAccessToken();
				return null;
			}
			JSONObject json = JSONObject.parseObject(response);
			if(json==null){
				log.debug("json为空:="+json);
				clearAccessToken();
				return null;
			}
			if(StringUtils.isNotBlank(json.getString("errcode"))){
				clearAccessToken();
				return null;
			}
			log.debug("获取到新的JsapiTicket:"+json.toJSONString());
			accessTokenExpireTime = json.getIntValue("expires_in");
			accessToken = json.getString("access_token");
			accessTokenTime=System.currentTimeMillis();
		} catch (Exception e) {
			LOG.error("getAccessToken-error", e);
			clearAccessToken();
		}
		LOG.debug("accessToken===" + accessToken);
		System.out.println("accessToken===" + accessToken);
		return accessToken;
	}
}

准备好固定的资源

	//公众号appid
	public static final String appId = "wxf546a5afce347110";
	//公众号appSecret
	public static final String appSecret = "wxf546a5afce347xxx";
	//商户号
	public static final String wxMerchantNo = "1272569000";
	//商户签名加密key key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
	public static final String wxMerchantApiKey = "xxxx";
	//私钥信息的证书文件
	private static final String certFilePath = "C:\\Users\\Public\\Downloads\\apiclient_cert.p12";

设计我们的表结构

这里只列出最核心的用户表、微信用户表、订单表,其他的表跟微信支付的业务关系不大。

用户表

public class UserInfo {
	/**
	 * 用户ID
	 */
	private Long id;
	/**
	 * 用户名称
	 */
	private String username;
	/**
	 * 手机号
	 */
	private String phoneNumber;
	/**
	 * 用户密码
	 */
	private String password;
	/**
	 * 有效性,-1删除,0禁用,1有效
	 */
	private Integer valid;
	/**
	 * 创建时间
	 */
	private Date createTime;
	/**
	 * 更新时间
	 */
	private Date updateTime;
}

微信用户表

public class WxUserInfo {
	/**
	 * 微信用户ID,
	 */
	private Long id;
	/**
	 * 用户ID,关联用户表的id,可以为空(除非你的公众号是不注册就不让用。。。)
	 */
	private Long userId;

	/**
	 * 是否关注,0=用户未关注公众号,1=用户关注了公众号
	 */
	private Integer subscribe;

	/**
	 * 微信appid
	 */
	private String appId;

	/**
	 * 微信openId
	 */
	private String openid;

	/**
	 * 昵称
	 */
	private String nickname;

	/**
	 * 用户的性别,值为1时是男性,值为2时是女性,值为0时是未知
	 */
	private Integer sex;

	/**
	 * 城市
	 */
	private String city;

	/**
	 * 国家
	 */
	private String country;

	/**
	 * 省份
	 */
	private String province;

	/**
	 * 头像
	 */
	private String headimgurl;

	/**
	 * 订阅时间
	 */
	private Long subscribeTime;

	/**
	 * 微信unionid,用来关联小程序
	 */
	private String unionid;

	/**
	 * 备注
	 */
	private String remark;

	/**
	 * 用户所在的分组ID
	 */
	private Integer groupid;

	/**
	 * 创建时间
	 */
	private Date createTime;

	/**
	 * 更新时间
	 */
	private Date updateTime;
}

订单表

public class OrderInfo {

	/**
	 * 主键 数据库列名:<order_id> 数据库类型:<bigint> 内容长度:<19> 默认值:<null> 是否允许空值:<false>
	 */
	private Long orderId;

	/**
	 * 商户订单号,也就是我们自己的订单号,需要唯一,微信要求最长不超过32个字符 数据库列名:<out_trade_no>
	 * 数据库类型:<varchar> 内容长度:<32> 默认值:<null> 是否允许空值:<false>
	 */
	private String outTradeNo;

	/**
	 * appid 数据库列名:<app_id> 数据库类型:<varchar> 内容长度:<50> 默认值:<null> 是否允许空值:<false>
	 */
	private String appId;

	/**
	 * 支付金额(微信要求单位:分) 数据库列名:<total_fee> 数据库类型:<varchar> 内容长度:<100> 默认值:<null>
	 * 是否允许空值:<false>
	 */
	private String totalFee;

	/**
	 * 请求ip 数据库列名:<spbill_create_ip> 数据库类型:<varchar> 内容长度:<512> 默认值:<null>
	 * 是否允许空值:<true>
	 */
	private String spbillCreateIp;

	/**
	 * 订单交易类型 取值如下:JSAPI,NATIVE,APP,WAP 数据库列名:<trade_type> 数据库类型:<varchar>
	 * 内容长度:<30> 默认值:<null> 是否允许空值:<false>
	 */
	private String tradeType;

	/**
	 * 商户号,微信支付商户唯一标识 数据库列名:<mch_id> 数据库类型:<varchar> 内容长度:<30> 默认值:<null>
	 * 是否允许空值:<false>
	 */
	private String mchId;

	/**
	 * openid 数据库列名:<open_id> 数据库类型:<varchar> 内容长度:<100> 默认值:<null>
	 * 是否允许空值:<true>
	 */
	private String openId;

	/**
	 * 微信订单号 数据库列名:<transaction_id> 数据库类型:<varchar> 内容长度:<64> 默认值:<null>
	 * 是否允许空值:<true>
	 */
	private String transactionId;

	/**
	 * 订单状态,1=生成待提交,2=生成已提交,3=支付完成,4=支付已取消 数据库列名:<status> 数据库类型:<tinyint>
	 * 内容长度:<3> 默认值:<null> 是否允许空值:<false>
	 */
	private Integer status;
	public static final Integer status_1 = 1;
	public static final Integer status_2 = 2;
	public static final Integer status_3 = 3;
	public static final Integer status_4 = 4;
	public static final Map<Integer, String> status_map = new LinkedHashMap<Integer, String>(4);
	static {
		status_map.put(status_1, "待生成");
		status_map.put(status_2, "待支付");
		status_map.put(status_3, "支付成功");
		status_map.put(status_4, "支付失败");
	}
	private String statusDetail;

	public String getStatusDetail() {
		statusDetail = status_map.get(status);
		return statusDetail;
	}

	public void setStatusDetail(String statusDetail) {
		this.statusDetail = statusDetail;
	}

	/**
	 * 退款状态 0:未退款 1:退款成功 2:退款失败 3:退款中
	 */
	private Integer refundStatus;
	private String refundStatusDesc;

	public String getRefundStatusDesc() {
		this.refundStatusDesc = REFUND_STATUS_MAP.get(this.refundStatus);
		return this.refundStatusDesc;
	}

	public static Map<Integer, String> REFUND_STATUS_MAP = new HashMap<Integer, String>();
	static {
		REFUND_STATUS_MAP.put(Constant.PAY_ORDER_FLOW_REFUND_STATUS_NOT, "待审核未退款");
		REFUND_STATUS_MAP.put(Constant.PAY_ORDER_FLOW_REFUND_STATUS_SUCCESS, "退款成功");
		REFUND_STATUS_MAP.put(Constant.PAY_ORDER_FLOW_REFUND_STATUS_FAIL, "退款失败");
		REFUND_STATUS_MAP.put(Constant.PAY_ORDER_FLOW_REFUND_STATUS_ING, "退款中");
		REFUND_STATUS_MAP.put(Constant.PAY_ORDER_FLOW_REFUND_STATUS_APPROVAL_AGREE, "审核通过待退款");
		REFUND_STATUS_MAP.put(Constant.PAY_ORDER_FLOW_REFUND_STATUS_APPROVAL_REJECT, "审核拒绝");
	}
	/**
	 * 退款时间
	 */
	private Date refundTime;

	/**
	 * 退款失败原因
	 */
	private String refundReason;

	private String refundOrderNo;

	/**
	 * 备注 数据库列名:<remark> 数据库类型:<varchar> 内容长度:<128> 默认值:<null> 是否允许空值:<true>
	 */
	private String remark;

	/**
	 * 数据创建时间 数据库列名:<create_date> 数据库类型:<timestamp> 内容长度:<0>
	 * 默认值:<CURRENT_TIMESTAMP> 是否允许空值:<false>
	 */
	private Date createDate;

	/**
	 * 数据修改时间 数据库列名:<update_date> 数据库类型:<timestamp> 内容长度:<0> 默认值:<null>
	 * 是否允许空值:<true>
	 */
	private Date updateDate;

	/**
	 * 微信昵称
	 */
	private String nickname;
	/**
	 * 回调时间
	 */
	private Date callbackDate;
}

开发

编写支付页面,使用wx.chooseWXPay调起微信支付界面

<html>
<script type="text/javascript">
$.ajax({
	url : '${ctx}/goods/sign.do',
	async : false,
	data : {
		'url' : window.location.href
	},
	dataType : 'json',
	type : 'post',
	success : function(result) {
		var data = result.data;
		if (data.appid == null || data.appid == "") {
			return;
		}
		wx.config({
			debug : false,
			appId : data.appid,
			timestamp : data.timestamp,
			nonceStr : data.nonceStr,
			signature : data.signature,
			jsApiList : [ "chooseWXPay" ]
		});
	}
});
wx.error(function(res) {
	alert("页面鉴权失败:"+res);
});
wx.ready(function() {
});

var flag = false;
function pay(btn) {
	if (flag == true) {
		alert("您已经支付过了,请不要重复支付哦!");
		return;
	}
	$.ajax({
		url : '${ctx}/wechat/pay.do',//后端提供wx.chooseWXPay的参数
		type : 'post',
		'data' : {
			"productId" : $("#productId").val(),
			"openId" : $("#openId").val()
		},
		dataType : 'json',
		aysnc : false,
		success : function(res) {
			if (res && res.code) {
				if(res.code == "200"){
					var data = res.data;
					wx.chooseWXPay({
					    'debug' : true,
                        'timestamp': data.timeStamp,
                        'nonceStr': data.nonceStr,
                        'package': 'prepay_id=' + data.prepayId, 
                        'signType': 'MD5',
                        'paySign': data.sign,
						success : function(res) {
                            flag = true;
							alert("支付成功");
							$(btn).prop("disabled", "disabled");
						},
						fail:function(err) {
							alert("系统错误:"+err);
						}
					});
				}else{
					alert("系统错误:"+res.msg);
				}
			} else {
				alert("系统错误:"+res);
			}
		}
	});
}
</script>
展示商品信息
<input id="productId" name="productId" value="${(product.productId)!''}" type="hidden"/>
<input id="openId" name="openId" value="${openId!''}" type="hidden"/>
<input type="button" id="payBtn" value="支付" onclick="pay(this)"/>
</html>

wx.chooseWXPay详解:

wx.chooseWXPay({
    timeStamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
    nonceStr: '', // 支付签名随机串,不长于 32 位
    package: '', // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=***)
    signType: '', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
    paySign: '', // 支付签名
    success: function (res) {
        // 支付成功后的回调函数
    }
});

wx.chooseWXPay有一个替代品:

WeixinJSBridge.invoke(
       'getBrandWCPayRequest', {
           "appId" : "wx2421b1c4370ec43b",     //公众号名称,由商户传入     
           "timeStamp":" 1395712654",         //时间戳,自1970年以来的秒数     
           "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //随机串     
           "package" : "prepay_id=u802345jgfjsdfgsdg888",     
           "signType" : "MD5",         //微信签名方式:     
           "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名 
       },
       function(res){     
           if(res.err_msg == "get_brand_wcpay_request:ok" ) {}     // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。 
       }
   ); 

编写后台支付接口/wechat/pay.do

	//订单商品清单
	public static class ItemDTO{//示例伪代码
		private long productId;
		private int count;
	}

	// 下单,生成prepay_id给前端H5,然后通过jsapi调用wx.chooseWXPay
	@RequestMapping(value = "/pay.do", method = RequestMethod.POST)
	@ResponseBody
	public Object pay(HttpServletRequest request, HttpServletResponse response, List<ItemDTO> items,String openId) throws IOException {
		String logPrefix = "支付>>>";
		LOG.debug(logPrefix + "参数,"+items);
		if (Utils.isEmpty(openId)) {
			return JsonResult.getFailResultAndLog("openId为空", LOG, logPrefix);
		}
		WxUserInfo wxUser = wxUserInfoMapper.selectByOpenId(openId);
		if (wxUser == null) {
			return JsonResult.getFailResultAndLog("微信用户未录入", LOG, logPrefix);
		}
		WeixinResponseDTO  weixinResponse= WechatUtil.pay(request, items, openId);
		if (weixinResponse == null) {
			return JsonResult.getFailResultAndLog("发起支付失败", LOG, logPrefix);
		}
		// LOG.debug(logPrefix + "前端需要的支付参数config=" + config);
		Map<String, String> wxResp = WechatUtil.getPayConfig(weixinResponse.getPrepay_id());
		return JsonResult.getSuccessResultByData(wxResp);
	}
public class WechatUtil {
	public static Map<String, String> getPayConfig(String prepay_id) {
		SortedMap<String, String> json = new TreeMap<String, String>();
		json.put("appId", appId);
		json.put("timeStamp", System.currentTimeMillis() + "");
		json.put("nonceStr", UUID.randomUUID().toString().replaceAll("-", ""));
		json.put("package", "prepay_id=" + prepay_id);
		json.put("signType", "MD5");
		String sign = getSign(json, wxMerchantApiKey);
		json.put("paySign", sign);
		log.debug("支付json:" + JSONObject.toJSONString(json));
		return json;
	}
}

调用微信支付统一接口unifiedorder

微信统一下单参数封装

public class WeixinOrderDTO {
	/**
	 * appId 必填
	 */
	private String appId;
	/**
	 * 商户号  必填
	 */
	private String machId;
	
	/**
	 * 设备号
	 */
	private String deviceInfo;
	/**
	 * 随机字符串   必填
	 */
	private String nonceStr;
	/**
	 * 签名  必填
	 */
	private String sign;
	/**
	 * 商品描述  必填
	 */
	private String body;
	/**
	 * 商品详情
	 */
	private String detail;
	/**
	 * 附加数据
	 */
	private String attach;
	/**
	 * 商户订单号   必填
	 */
	private String outTradeNo;
	/**
	 *货币类型
	 */
	private String feeType;
	/**
	 * 总金额   必填
	 */
	private int totalFee;
	/**
	 * 终端Ip   必填
	 */
	private String spbillCreateIp;
	/**
	 * 交易起始时间
	 */
	private String timeStart;
	/**
	 * 交易结束时间
	 */
	private String timeExpire;
	/**
	 * 商品标记
	 */
	private String goodsTag;
	/**
	 * 通知地址  必填
	 */
	private String notifyUrl;
	/**
	 * 交易类型  必填
	 */
	private String tradeType;
	/**
	 * 商品ID
	 */
	private String productId;
	/**
	 * 指定支付方式
	 */
	private String limitPay;
	/**
	 * 用户标识
	 */
	private String openId;
	
	/**
	 * 微信退款商户唯一订单号
	 */
	private String outRefundNo;
	
	public String getAppId() {
		return appId;
	}
	public void setAppId(String appId) {
		this.appId = appId;
	}
	public String getMachId() {
		return machId;
	}
	public void setMachId(String machId) {
		this.machId = machId;
	}
	public String getDeviceInfo() {
		return deviceInfo;
	}
	public void setDeviceInfo(String deviceInfo) {
		this.deviceInfo = deviceInfo;
	}
	public String getNonceStr() {
		return nonceStr;
	}
	public void setNonceStr(String nonceStr) {
		this.nonceStr = nonceStr;
	}
	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 getDetail() {
		return detail;
	}
	public void setDetail(String detail) {
		this.detail = detail;
	}
	public String getAttach() {
		return attach;
	}
	public void setAttach(String attach) {
		this.attach = attach;
	}
	public String getOutTradeNo() {
		return outTradeNo;
	}
	public void setOutTradeNo(String outTradeNo) {
		this.outTradeNo = outTradeNo;
	}
	public String getFeeType() {
		return feeType;
	}
	public void setFeeType(String feeType) {
		this.feeType = feeType;
	}
	public int getTotalFee() {
		return totalFee;
	}
	public void setTotalFee(int totalFee) {
		this.totalFee = totalFee;
	}
	public String getSpbillCreateIp() {
		return spbillCreateIp;
	}
	public void setSpbillCreateIp(String spbillCreateIp) {
		this.spbillCreateIp = spbillCreateIp;
	}
	public String getTimeStart() {
		return timeStart;
	}
	public void setTimeStart(String timeStart) {
		this.timeStart = timeStart;
	}
	public String getTimeExpire() {
		return timeExpire;
	}
	public void setTimeExpire(String timeExpire) {
		this.timeExpire = timeExpire;
	}
	public String getGoodsTag() {
		return goodsTag;
	}
	public void setGoodsTag(String goodsTag) {
		this.goodsTag = goodsTag;
	}
	public String getNotifyUrl() {
		return notifyUrl;
	}
	public void setNotifyUrl(String notifyUrl) {
		this.notifyUrl = notifyUrl;
	}
	public String getTradeType() {
		return tradeType;
	}
	public void setTradeType(String tradeType) {
		this.tradeType = tradeType;
	}
	public String getProductId() {
		return productId;
	}
	public void setProductId(String productId) {
		this.productId = productId;
	}
	public String getLimitPay() {
		return limitPay;
	}
	public void setLimitPay(String limitPay) {
		this.limitPay = limitPay;
	}
	public String getOpenId() {
		return openId;
	}
	public void setOpenId(String openId) {
		this.openId = openId;
	}
	public String getOutRefundNo() {
		return outRefundNo;
	}
	public void setOutRefundNo(String outRefundNo) {
		this.outRefundNo = outRefundNo;
	}
	
}

微信统一下单接口调用

public class WechatUtil {

		// 商户号
	public static final String wxMerchantNo = "1272569000";
	/**
	 * 商户签名加密key key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置
	 */
	public static final String wxMerchantApiKey = "xxxx";

	/**
	 * 微信二维码支付-微信统一下单
	 */
	public static WeixinResponseDTO pay(HttpServletRequest request, List<ItemDTO> products, String openId) {
		String outTradeNo = genOutTradeNo();
		String wxOrderShowName = "赶快付款,来不及解释了!";
		LOG.debug("微信统一下单>>微信支付流水单号:" + outTradeNo + ":==" + products);
		OrderInfo orderInfo = new OrderInfo();
		orderInfo.setStatus(OrderInfo.status_1);
		orderInfo.setOutTradeNo(outTradeNo);
		orderInfo.setOpenId(openId);

		// 根据商品和数量计算出金额
		// ...
		// 生成订单插入数据库
		orderInfoMapper.insert(orderInfo);

		WeixinOrderDTO wxOrder = new WeixinOrderDTO();
		wxOrder.setAppId(appId);
		wxOrder.setBody(wxOrderShowName);// 支付界面显示的标题
		wxOrder.setOutTradeNo(outTradeNo);// 我们自己的订单号
		// 商户号 必填
		wxOrder.setMachId(wxMerchantNo);
		wxOrder.setNonceStr(UUID.randomUUID().toString().replaceAll("-", ""));
		String notifyUrl = "http://www.xuexibisai.com/wechat/callback.do";
		wxOrder.setNotifyUrl(notifyUrl);
		wxOrder.setOpenId(openId);

		wxOrder.setTotalFee(new BigDecimal(orderInfo.getTotalFee()).multiply(new BigDecimal(100)).intValue());// 金额单位元转分
		wxOrder.setTradeType("JSAPI");
		wxOrder.setSpbillCreateIp("127.0.0.1");
		String str = unifiedorder(wxOrder, wxMerchantApiKey);
		LOG.debug("微信统一下单>>微信返回数据:" + str);
		if (StringUtils.isBlank(str)) {
			LOG.error("微信统一下单>>微信统一下单失败,微信未返回数据");
			return null;
		}
		XStream xs = new XStream(new DomDriver());
		xs.alias("xml", WeixinResponseDTO.class);
		WeixinResponseDTO weixinResponse = (WeixinResponseDTO) xs.fromXML(str);
		if (weixinResponse == null) {
			LOG.error("微信统一下单>>解析微信返回数据失败");
			return weixinResponse;
		}
		if (StringUtils.isBlank(weixinResponse.getResult_code()) || StringUtils.isBlank(weixinResponse.getReturn_code()) || !weixinResponse.getResult_code().equals("SUCCESS")
				|| !weixinResponse.getReturn_code().equals("SUCCESS")) {
			LOG.error("微信统一下单>>微信返回数据return_code为失败");
			return weixinResponse;
		}
		boolean flag = validateResponseSign(weixinResponse, wxMerchantApiKey);
		if (!flag) {
			LOG.error("微信统一下单>>微信返回数据apikey校验失败");
			return weixinResponse;
		}

		String prepay_id = weixinResponse.getPrepay_id();
		LOG.debug("微信统一下单>>prepay_id:" + prepay_id);
		if (!Utils.isEmptyTrim(prepay_id)) {
			orderInfo.setStatus(OrderInfo.status_2);
			orderInfoMapper.update(orderInfo);
		}
		return weixinResponse;
	}

	public static boolean validateResponseSign(WeixinResponseDTO weixinResponse, String apiKey) {
		Map<String, String> map = new TreeMap<String, String>();
		if (StringUtils.isNotBlank(weixinResponse.getAppid())) {
			map.put("appid", weixinResponse.getAppid());
		}
		if (StringUtils.isNotBlank(weixinResponse.getMch_id())) {
			map.put("mch_id", weixinResponse.getMch_id());
		}
		if (StringUtils.isNotBlank(weixinResponse.getNonce_str())) {
			map.put("nonce_str", weixinResponse.getNonce_str());
		}
		if (StringUtils.isNotBlank(weixinResponse.getPrepay_id())) {
			map.put("prepay_id", weixinResponse.getPrepay_id());
		}
		if (StringUtils.isNotBlank(weixinResponse.getResult_code())) {
			map.put("result_code", weixinResponse.getResult_code());
		}
		if (StringUtils.isNotBlank(weixinResponse.getReturn_code())) {
			map.put("return_code", weixinResponse.getReturn_code());
		}
		if (StringUtils.isNotBlank(weixinResponse.getTrade_type())) {
			map.put("trade_type", weixinResponse.getTrade_type());
		}
		if (StringUtils.isNotBlank(weixinResponse.getReturn_msg())) {
			map.put("return_msg", weixinResponse.getReturn_msg());
		}
		if (StringUtils.isNotBlank(weixinResponse.getCode_url())) {
			map.put("code_url", weixinResponse.getCode_url());
		}
		if (StringUtils.isNotBlank(weixinResponse.getMweb_url())) {
			map.put("mweb_url", weixinResponse.getMweb_url());
		}
		if (StringUtils.isBlank(weixinResponse.getSign())) {
			return false;
		}
		String sign = getSign(map, apiKey);
		log.debug("-----------对比后的sign1111" + sign);
		return sign.equals(weixinResponse.getSign());
	}

	public static final String unifiedorder_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
	/**
	 * 微信支付下单
	 */
	public static String unifiedorder(WeixinOrderDTO weixinOrder, String key) {
		Map<String, String> map = new TreeMap<String, String>();
		StringBuffer sb = new StringBuffer();
		sb.append("<xml>");
		sb.append("<appid><![CDATA[" + weixinOrder.getAppId() + "]]></appid>");
		if (StringUtils.isNotBlank(weixinOrder.getBody())) {
			sb.append("<body><![CDATA[" + weixinOrder.getBody() + "]]></body>");
			map.put("body", weixinOrder.getBody());
		}
		sb.append("<mch_id><![CDATA[" + weixinOrder.getMachId() + "]]></mch_id>");
		sb.append("<nonce_str><![CDATA[" + weixinOrder.getNonceStr() + "]]></nonce_str>");
		sb.append("<notify_url><![CDATA[" + weixinOrder.getNotifyUrl() + "]]></notify_url>");
		if (!StringUtils.isEmpty(weixinOrder.getOpenId())) {
			sb.append("<openid><![CDATA[" + weixinOrder.getOpenId() + "]]></openid>");
		}
		sb.append("<out_trade_no><![CDATA[" + weixinOrder.getOutTradeNo() + "]]></out_trade_no>");
		sb.append("<spbill_create_ip><![CDATA[" + weixinOrder.getSpbillCreateIp() + "]]></spbill_create_ip>");
		sb.append("<total_fee><![CDATA[" + weixinOrder.getTotalFee() + "]]></total_fee>");// 单位是分,不是元
		sb.append("<trade_type><![CDATA[" + weixinOrder.getTradeType() + "]]></trade_type>");

		map.put("appid", weixinOrder.getAppId());
		map.put("mch_id", weixinOrder.getMachId());
		map.put("nonce_str", weixinOrder.getNonceStr());
		map.put("notify_url", weixinOrder.getNotifyUrl());
		if (!StringUtils.isEmpty(weixinOrder.getOpenId())) {
			map.put("openid", weixinOrder.getOpenId());
		}
		map.put("out_trade_no", weixinOrder.getOutTradeNo());
		map.put("spbill_create_ip", weixinOrder.getSpbillCreateIp() + "");
		map.put("total_fee", weixinOrder.getTotalFee() + "");
		map.put("trade_type", weixinOrder.getTradeType());
		String sign = getSign(map, key);
		sb.append("<sign><![CDATA[" + sign + "]]></sign>");
		sb.append("</xml>");
		String str = null;
		log.debug("url:" + unifiedorder_url + "xml:" + sb.toString());
		try {
			str = postByBody(unifiedorder_url, sb.toString(), "text/xml;charset=utf-8");
		} catch (HttpException e) {
			log.error(e.getMessage(), e);
			e.printStackTrace();
		} catch (IOException e) {
			log.error(e.getMessage(), e);
			e.printStackTrace();
		}
		return str;
	}
}

统一下单返回结果示例:

"<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg>"+
  "<appid><![CDATA[wxf546a5afce347110]]></appid>"+
  "<mch_id><![CDATA[1272569110]]></mch_id>"+
  "<nonce_str><![CDATA[LHxao3Tj84t1xsKi]]></nonce_str>"+
  "<sign><![CDATA[358FD39B7F0B7333666CA55BDBC1C88F]]></sign>"+
  "<result_code><![CDATA[SUCCESS]]></result_code>"+
  "<transaction_id><![CDATA[4200000167201808189301147765]]></transaction_id>"+
  "<out_trade_no><![CDATA[180818456205632937]]></out_trade_no>"+
  "<out_refund_no><![CDATA[180818457380003106]]></out_refund_no>"+
  "<refund_id><![CDATA[50000507922018081805532655669]]></refund_id>"+
  "<refund_channel><![CDATA[]]></refund_channel>"+
  "<refund_fee>6000</refund_fee>"+
  "<coupon_refund_fee>12</coupon_refund_fee>"+
  "<total_fee>6000</total_fee>"+
  "<cash_fee>5988</cash_fee>"+
  "<coupon_refund_count>1</coupon_refund_count>"+
  "<coupon_refund_fee_0>12</coupon_refund_fee_0>"+
  "<coupon_refund_id_0><![CDATA[2000000042245560146]]></coupon_refund_id_0>"+
  "<cash_refund_fee>5988</cash_refund_fee>"+
"</xml>"

统一下单返回结果封装

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.io.xml.DomDriver;

@XStreamAlias("xml")
public class WeixinResponseDTO {

	@XStreamAlias("return_code")
	private String return_code;
	@XStreamAlias("return_msg")
	private String return_msg;
	@XStreamAlias("appid")
	private String appid;
	@XStreamAlias("mch_appid")
	private String mch_appid;
	@XStreamAlias("mchid")
	private String mchid;
	@XStreamAlias("device_info")
	private String device_info;
	@XStreamAlias("partner_trade_no")
	private String partner_trade_no;
	@XStreamAlias("payment_no")
	private String payment_no;
	@XStreamAlias("payment_time")
	private String payment_time;
	
	
	
	@XStreamAlias("mch_id")
	private String mch_id;
	@XStreamAlias("nonce_str")
	private String nonce_str;
	@XStreamAlias("sign")
	private String sign;
	@XStreamAlias("result_code")
	private String result_code;
	@XStreamAlias("prepay_id")
	private String prepay_id;
	@XStreamAlias("trade_type")
	private String trade_type;
	
	//二维码
	@XStreamAlias("code_url")
	private String code_url;
	//app支付
	@XStreamAlias("mweb_url")	
	private String mweb_url;
	//退款参数
	@XStreamAlias("err_code")
	private String err_code;	//错误
	@XStreamAlias("err_code_des")
	private String err_code_des;	//错误信息
	@XStreamAlias("transaction_id")
	private String transaction_id;	//微信订单号
	@XStreamAlias("out_trade_no")
	private String out_trade_no;	//商户系统内部的订单号
	@XStreamAlias("out_refund_no")
	private String out_refund_no;	//商户退款单号
	@XStreamAlias("refund_id")
	private String refund_id;		//微信退款单号
	@XStreamAlias("refund_channel")
	private String refund_channel;	//退款渠道
	@XStreamAlias("refund_fee")
	private int refund_fee;			//退款金额
	@XStreamAlias("coupon_refund_fee")
	private int coupon_refund_fee;	//代金券或立减优惠退款金额=订单金额-现金退款金额,注意:立减优惠金额不会退回
	@XStreamAlias("total_fee")
	private int total_fee;			//订单总金额
	@XStreamAlias("cash_fee")
	private int cash_fee;			//现金支付金额
	@XStreamAlias("coupon_refund_count")
	private int coupon_refund_count;	//代金券或立减优惠使用数量	0
	@XStreamAlias("cash_refund_fee")
	private int cash_refund_fee;		//代金券或立减优惠退款金额	0
	//微信使用代金券购买退款时返回参数会增加,导致xml解析出错,退款失败 zw 2018年8月23日
	@XStreamAlias("coupon_refund_fee_0")
	private int coupon_refund_fee_0;
	@XStreamAlias("coupon_refund_id_0")
	private String coupon_refund_id_0;
	public String getReturn_code() {
		return return_code;
	}
	public void setReturn_code(String return_code) {
		this.return_code = return_code;
	}
	public String getReturn_msg() {
		return return_msg;
	}
	public void setReturn_msg(String return_msg) {
		this.return_msg = return_msg;
	}
	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 getResult_code() {
		return result_code;
	}
	public void setResult_code(String result_code) {
		this.result_code = result_code;
	}
	public String getPrepay_id() {
		return prepay_id;
	}
	public void setPrepay_id(String prepay_id) {
		this.prepay_id = prepay_id;
	}
	public String getTrade_type() {
		return trade_type;
	}
	public void setTrade_type(String trade_type) {
		this.trade_type = trade_type;
	}
	public String getTransaction_id() {
		return transaction_id;
	}
	public void setTransaction_id(String transaction_id) {
		this.transaction_id = transaction_id;
	}
	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 String getOut_refund_no() {
		return out_refund_no;
	}
	public void setOut_refund_no(String out_refund_no) {
		this.out_refund_no = out_refund_no;
	}
	public String getRefund_id() {
		return refund_id;
	}
	public void setRefund_id(String refund_id) {
		this.refund_id = refund_id;
	}
	public String getRefund_channel() {
		return refund_channel;
	}
	public void setRefund_channel(String refund_channel) {
		this.refund_channel = refund_channel;
	}
	public int getRefund_fee() {
		return refund_fee;
	}
	public void setRefund_fee(int refund_fee) {
		this.refund_fee = refund_fee;
	}
	public int getCoupon_refund_fee() {
		return coupon_refund_fee;
	}
	public void setCoupon_refund_fee(int coupon_refund_fee) {
		this.coupon_refund_fee = coupon_refund_fee;
	}
	public int getTotal_fee() {
		return total_fee;
	}
	public void setTotal_fee(int total_fee) {
		this.total_fee = total_fee;
	}
	public int getCash_fee() {
		return cash_fee;
	}
	public void setCash_fee(int cash_fee) {
		this.cash_fee = cash_fee;
	}
	public int getCoupon_refund_count() {
		return coupon_refund_count;
	}
	public void setCoupon_refund_count(int coupon_refund_count) {
		this.coupon_refund_count = coupon_refund_count;
	}
	public int getCash_refund_fee() {
		return cash_refund_fee;
	}
	public void setCash_refund_fee(int cash_refund_fee) {
		this.cash_refund_fee = cash_refund_fee;
	}
	public String getCode_url() {
		return code_url;
	}
	public void setCode_url(String code_url) {
		this.code_url = code_url;
	}
	public boolean isSuccess(){
		return "SUCCESS".equalsIgnoreCase(this.getReturn_code()) && "SUCCESS".equalsIgnoreCase(this.getResult_code());
	}
	public String getErr_code() {
		return err_code;
	}
	public void setErr_code(String err_code) {
		this.err_code = err_code;
	}
	public String getErr_code_des() {
		return err_code_des;
	}
	public void setErr_code_des(String err_code_des) {
		this.err_code_des = err_code_des;
	}
	
	public int getCoupon_refund_fee_0() {
		return coupon_refund_fee_0;
	}
	public void setCoupon_refund_fee_0(int coupon_refund_fee_0) {
		this.coupon_refund_fee_0 = coupon_refund_fee_0;
	}
	public String getCoupon_refund_id_0() {
		return coupon_refund_id_0;
	}
	public void setCoupon_refund_id_0(String coupon_refund_id_0) {
		this.coupon_refund_id_0 = coupon_refund_id_0;
	}
	
	
	public String getMweb_url() {
		return mweb_url;
	}
	public void setMweb_url(String mweb_url) {
		this.mweb_url = mweb_url;
	}
	public String getMch_appid() {
		return mch_appid;
	}
	public void setMch_appid(String mch_appid) {
		this.mch_appid = mch_appid;
	}
	public String getMchid() {
		return mchid;
	}
	public void setMchid(String mchid) {
		this.mchid = mchid;
	}
	public String getDevice_info() {
		return device_info;
	}
	public void setDevice_info(String device_info) {
		this.device_info = device_info;
	}
	public String getPartner_trade_no() {
		return partner_trade_no;
	}
	public void setPartner_trade_no(String partner_trade_no) {
		this.partner_trade_no = partner_trade_no;
	}
	public String getPayment_no() {
		return payment_no;
	}
	public void setPayment_no(String payment_no) {
		this.payment_no = payment_no;
	}
	public String getPayment_time() {
		return payment_time;
	}
	public void setPayment_time(String payment_time) {
		this.payment_time = payment_time;
	}
}

回调接口/wechat/callback.do

	/**
	 * 账单结算微信支付-支付回调
	 * 
	 * @param body
	 *            微信传递的消息体
	 * @author tangzhichao 20200922 新增
	 */
	@RequestMapping(value = "/callback.do")
	public void balancePayNotify(HttpServletRequest request, HttpServletResponse resp) throws IOException {
		LOG.debug("账单结算微信二维码支付---支付回调...");
		try {
			String param = WebUtil.getParams(request);
			if (StringUtils.isBlank(param)) {
				LOG.error("支付回调>>>回调信息为空");
				WechatUtil.fail(resp);
				return;
			}
			XStream xs = new XStream(new DomDriver());
			xs.alias("xml", WeChatBuyPostDTO.class);
			WeChatBuyPostDTO weChatBuyPost = (WeChatBuyPostDTO) xs.fromXML(param);
			if (weChatBuyPost == null) {
				LOG.error("支付回调>>>回调信息解析失败");
				WechatUtil.fail(resp);
				return;
			}
			String outTradeNo = weChatBuyPost.getOut_trade_no();
			if (Utils.isEmptyTrim(outTradeNo)) {
				LOG.error("支付回调>>>回调信息out_trade_no为空");
				WechatUtil.fail(resp);
				return;
			}
			LOG.debug("支付回调>>>订单Id:{}", outTradeNo);
			OrderInfo wxPayMonthBalanceOrder = orderInfoMapper.selectByOutTradeNo(outTradeNo);// 根据我们的订单号去数据库查出对应的订单
			if (wxPayMonthBalanceOrder == null) {
				LOG.error("支付回调>>>未找到订单号" + outTradeNo);
				WechatUtil.fail(resp);
				return;
			}
			if (StringUtils.isBlank(weChatBuyPost.getResult_code()) || StringUtils.isBlank(weChatBuyPost.getReturn_code()) || !weChatBuyPost.getResult_code().equals("SUCCESS")) {
				LOG.error("支付回调>>>回调信息result_code返回失败,具体信息:{}", JSONObject.toJSONString(weChatBuyPost));
				failAndUp(resp, wxPayMonthBalanceOrder, weChatBuyPost);
				return;
			}
			if (!WechatUtil.verifySign(param, WechatUtil.wxMerchantApiKey)) {
				LOG.error("支付回调>>>回调信息apikey校验失败");
				failAndUp(resp, wxPayMonthBalanceOrder, weChatBuyPost);
				return;
			}
			LOG.debug("支付回调>>>debug-判断status>>>" + wxPayMonthBalanceOrder.getStatus());
			if (WxPayMonthBalanceOrder.status_2.equals(wxPayMonthBalanceOrder.getStatus())) {
				// ...你的业务逻辑
			} else {
				LOG.debug("支付回调>>>重复回调...");
			}
			LOG.debug("支付回调>>>微信回调成功");
		} catch (Exception e) {
			LOG.error("支付回调>>>异常", e);
			failByCallback(resp);
			return;
		}
		LOG.debug("支付回调>>>debug-响应微信文件流>>>");
		WechatUtil.outText(resp, "text/xml", "utf-8", "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>");
	}

	private void failAndUp(HttpServletResponse resp, OrderInfo orderInfo, WeChatBuyPostDTO weChatBuyPost) throws IOException {
		orderInfo.setStatus(OrderInfo.status_4);
		orderInfoMapper.update(orderInfo);
		WechatUtil.fail(resp);
	}
public class WechatUtil {
	public static boolean verifySign(String xml, String key){
		Map<String,String> params = new TreeMap<String,String>();
		String primtiveSign = "";
		try {
			Document doc = DocumentHelper.parseText(xml);
			Element root = doc.getRootElement();
			Iterator<Element> it = root.elementIterator();
			while(it.hasNext()){
				Element e = it.next();
				if("sign".equals(e.getName().trim())){
					primtiveSign = e.getTextTrim();
					continue;
				}
				if(StringUtils.isNotBlank(e.getText())){
					params.put(e.getName(), e.getTextTrim());
				}
			}
			String newSign = getSign(params,key);
			boolean res =  newSign.equals(primtiveSign);
			log.debug("WeiXinManager:verifySign---->支付成功检查是否合法sign:" + res);
			return res;
		} catch (DocumentException e) {
			log.error("WeiXinManager:verifySign--->message = {}, trace = {}", e.getMessage(), e);
		}
		return false;
	}
}

退款WechatUtil .refund(OrderInfo orderInfo)

public class WechatUtil {
	private static final String certFilePath = "C:\\Users\\Public\\Downloads\\apiclient_cert.p12";
	public static void refund(OrderInfo orderInfo){
		if (StringUtils.isBlank(orderInfo.getRefundOrderNo())) {
			orderInfo.setRefundOrderNo(genRefundOrderNo());

		}
		orderInfo.setRefundStatus(Constant.PAY_ORDER_FLOW_REFUND_STATUS_ING);
		int row = orderInfoMapper.updateByPrimaryKey(orderInfo);
		if (row == 0) {
			LOG.error("更新退款 商户订单号失败:{}", orderInfo.getOrderId());
			return;
		}
		LOG.debug("修改退款操作,将订单改为退款中:" + orderInfo.getOrderId());
		WeixinOrderDTO order = new WeixinOrderDTO();
		order.setAppId(appId);
		order.setMachId(wxMerchantNo);
		order.setOutTradeNo(orderInfo.getOutTradeNo());
		order.setNonceStr(UUID.randomUUID().toString().replaceAll("-", ""));
		order.setTotalFee(orderInfo.getTotalFee().multiply(new BigDecimal(100)).intValue());
		//微信对此退款要求使用同一个商户退款订单号
		order.setOutRefundNo(orderInfo.getRefundOrderNo());
		String str = refund(order, wxMerchantApiKey, certFilePath);
		if (StringUtils.isEmpty(str)) {
			LOG.error("退款失败,请求退款返回异常");
			orderInfo.setRefundReason("退款失败,请求退款返回异常");
		}
		LOG.debug("退款请求返回:" + str);
		XStream xs = new XStream(new DomDriver());
		xs.alias("xml", WeixinResponseDTO.class);
		WeixinResponseDTO weixinResponse = (WeixinResponseDTO) xs.fromXML(str);
		if (weixinResponse == null) {
			LOG.error("解析xml失败");
			orderInfo.setRefundReason("退款失败,退款结果解析失败");
			// return;
		}
		boolean flag = validateResponseSignByRefund(weixinResponse, wxMerchantApiKey);
		if (!flag) {
			LOG.error("签名错误");
			/**
			 * 注释return,此处会造成微信返回失败时直接return,出现退款失败的订单一直处于退款中状态,无法重新退款
			 */
			// return;
			orderInfo.setRefundReason("签名错误");
		}
		// 微信退款单号
		String refundId = weixinResponse.getRefund_id();
		LOG.debug("微信退款单号:" + refundId);
		if (weixinResponse.isSuccess()) {
			orderInfo.setRefundStatus(Constant.PAY_ORDER_FLOW_REFUND_STATUS_SUCCESS);
			orderInfo.setRefundReason(weixinResponse.getReturn_msg());
			LOG.debug("微信退款成功" + orderInfo.getOrderId());
			//...你的业务逻辑
		}else {
			orderInfo.setRefundStatus(Constant.PAY_ORDER_FLOW_REFUND_STATUS_FAIL);
			//注释,此处微信返回的return_msg只是通信的结果信息,并不是退款失败的原因
			// orderInfo.setRefundReason(weixinResponse.getReturn_msg());
			if (weixinResponse.getResult_code().equalsIgnoreCase("FAIL")) {
				orderInfo.setRefundReason(weixinResponse.getErr_code_des());
			}
		}
		orderInfo.setRefundTime(new Date());
		orderInfoMapper.updateByPrimaryKey(orderInfo);
	}
	
	private static int se = 0;
	public static synchronized String genRefundOrderNo() {
		String nextFrozeId = "";
		if (se > 99)
			se = 0;
		nextFrozeId = new SimpleDateFormat("yyyyMMdd").format(new java.util.Date()).substring(2, 8)
				+ ("" + new java.util.Date().getTime()).substring(3, 13) + createSerial("" + se, 2);
		se++;
		if (nextFrozeId.length() != 18)
			throw new RuntimeException("申请号长度错误[" + nextFrozeId + "]");
		return nextFrozeId;
	}


	public static String createSerial(String src, int len) {
		String dest = "";
		if (src.length() >= len) {
			dest = src.substring(0, len);
		} else {
			dest = createSameChar("0", len - src.length()) + src;
		}
		return dest;
	}

	public static String createSameChar(String src, int len) {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < len; i++) {
			sb.append(src);
		}
		return sb.toString();
	}
	
	/**
	 * 微信退款校验Sign
	 * @return
	 */
	public static boolean validateResponseSignByRefund(WeixinResponseDTO weixinResponse, String apiKey){
		Map<String, String> map = new TreeMap<String, String>();
		if (StringUtils.isNotBlank(weixinResponse.getAppid())) {
			map.put("appid", weixinResponse.getAppid());
		}
		if (StringUtils.isNotBlank(weixinResponse.getMch_id())) {
			map.put("mch_id", weixinResponse.getMch_id());
		}
		if (StringUtils.isNotBlank(weixinResponse.getNonce_str())) {
			map.put("nonce_str", weixinResponse.getNonce_str());
		}
		if (StringUtils.isNotBlank(weixinResponse.getResult_code())) {
			map.put("result_code", weixinResponse.getResult_code());
		}
		if (StringUtils.isNotBlank(weixinResponse.getReturn_code())) {
			map.put("return_code", weixinResponse.getReturn_code());
		}
		if (StringUtils.isNotBlank(weixinResponse.getReturn_msg())) {
			map.put("return_msg", weixinResponse.getReturn_msg());
		}
		if (StringUtils.isNotBlank(weixinResponse.getTransaction_id())) {
			map.put("transaction_id", weixinResponse.getTransaction_id());
		}
		if (StringUtils.isNotBlank(weixinResponse.getOut_trade_no())) {
			map.put("out_trade_no", weixinResponse.getOut_trade_no());
		}
		if (StringUtils.isNotBlank(weixinResponse.getOut_refund_no())) {
			map.put("out_refund_no", weixinResponse.getOut_refund_no());
		}
		if (StringUtils.isNotBlank(weixinResponse.getRefund_id())) {
			map.put("refund_id", weixinResponse.getRefund_id());
		}
		if (StringUtils.isNotBlank(weixinResponse.getRefund_channel())) {
			map.put("refund_channel", weixinResponse.getRefund_channel());
		}
		if (weixinResponse.getRefund_fee() >= 0) {
			map.put("refund_fee", weixinResponse.getRefund_fee()+"");
		}
		if (weixinResponse.getCoupon_refund_fee() >= 0) {
			map.put("coupon_refund_fee", weixinResponse.getCoupon_refund_fee()+"");
		}
		if (weixinResponse.getTotal_fee() >= 0) {
			map.put("total_fee", weixinResponse.getTotal_fee()+"");
		}
		if (weixinResponse.getCash_fee() >= 0) {
			map.put("cash_fee", weixinResponse.getCash_fee()+"");
		}
		if (weixinResponse.getCoupon_refund_count() >= 0) {
			map.put("coupon_refund_count", weixinResponse.getCoupon_refund_count()+"");
		}
		if (weixinResponse.getCash_refund_fee() >= 0) {
			map.put("cash_refund_fee", weixinResponse.getCash_refund_fee()+"");
		}
		if (StringUtils.isBlank(weixinResponse.getSign())) {
			return false;
		}
		String sign = getSign(map, apiKey);
		LOG.debug("sign:"+sign);
		LOG.debug("requestSign:"+weixinResponse.getSign());
		return sign.equals(weixinResponse.getSign());
	}
	

	public static final String refund_url = "https://api.mch.weixin.qq.com/secapi/pay/refund";
	/**
	 * 微信退款资金来源:refund_account
	 * REFUND_SOURCE_UNSETTLED_FUNDS:未结算资金退款(默认使用未结算资金退款),这种资金来源,只能使用订单收入的金额,不确定性较大,容易出现退款失败
	 * REFUND_SOURCE_RECHARGE_FUNDS:可用余额退款,这种方式稳定,账户资金不够时直接充值就可以了
	 */
	public static final String REFUND_SOURCE_UNSETTLED_FUNDS = "REFUND_SOURCE_UNSETTLED_FUNDS";
	public static final String REFUND_SOURCE_RECHARGE_FUNDS = "REFUND_SOURCE_RECHARGE_FUNDS";

	/**
	 * 退款请求
	 * 
	 * @param weixinOrder
	 *            退款参数
	 * @param key
	 *            签名key
	 * @param path
	 *            签权路径
	 * @return
	 */
	public static String refund(WeixinOrderDTO weixinOrder, String key, String path) {
		Map<String, String> map = new TreeMap<String, String>();

		StringBuffer sb = new StringBuffer();
		sb.append("<xml>");
		sb.append("<appid><![CDATA[" + weixinOrder.getAppId() + "]]></appid>");
		sb.append("<mch_id><![CDATA[" + weixinOrder.getMachId() + "]]></mch_id>");
		sb.append("<nonce_str><![CDATA[" + weixinOrder.getNonceStr() + "]]></nonce_str>");
		sb.append("<op_user_id><![CDATA[" + weixinOrder.getMachId() + "]]></op_user_id>");
		sb.append("<out_refund_no><![CDATA[" + weixinOrder.getOutRefundNo() + "]]></out_refund_no>");
		sb.append("<out_trade_no><![CDATA[" + weixinOrder.getOutTradeNo() + "]]></out_trade_no>");
		sb.append("<refund_fee><![CDATA[" + weixinOrder.getTotalFee() + "]]></refund_fee>");
		// 退款资金来源
		sb.append("<refund_account><![CDATA[" + REFUND_SOURCE_RECHARGE_FUNDS + "]]></refund_account>");
		sb.append("<total_fee><![CDATA[" + weixinOrder.getTotalFee() + "]]></total_fee>");
		sb.append("<transaction_id><![CDATA[]]></transaction_id>");
		map.put("appid", weixinOrder.getAppId());
		map.put("mch_id", weixinOrder.getMachId());
		map.put("nonce_str", weixinOrder.getNonceStr());
		map.put("op_user_id", weixinOrder.getMachId());
		map.put("out_refund_no", weixinOrder.getOutRefundNo());
		map.put("out_trade_no", weixinOrder.getOutTradeNo());
		map.put("refund_account", REFUND_SOURCE_RECHARGE_FUNDS + "");
		map.put("refund_fee", weixinOrder.getTotalFee() + "");
		map.put("total_fee", weixinOrder.getTotalFee() + "");
		map.put("transaction_id", "");
		String sign = getSign(map, key);
		sb.append("<sign><![CDATA[" + sign + "]]></sign>");
		sb.append("</xml>");
		String str = null;
		LOG.debug("url:" + refund_url + "xml:" + sb.toString());
		try {
			str = postByBodyWithCert(refund_url, sb.toString(), "application/xml; charset=UTF-8", path);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
		}
		return str;
	}

	public static String postByBodyWithCert(String url, String body, String contentType, String certPath) throws HttpException, Exception {
		KeyStore keyStore = KeyStore.getInstance("PKCS12");
		File file = new File(certPath);
		String fileName = file.getName();
		fileName = fileName.substring(0, fileName.lastIndexOf("."));
		FileInputStream instream = new FileInputStream(file);
		try {

			keyStore.load(instream, fileName.toCharArray());
		} finally {
			instream.close();
		}
		// Trust own CA and all self-signed certs
		SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, fileName.toCharArray()).build();
		// Allow TLSv1 protocol only
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
				SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

		RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(TIMEOUT_SEC * 1000).setSocketTimeout(TIMEOUT_SEC * 1000).setConnectTimeout(1000).build();

		CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
		StringBuilder sb = new StringBuilder();
		try {

			HttpPost httpPost = new HttpPost(url);
			httpPost.setConfig(requestConfig);
			httpPost.addHeader("Content-Type", contentType);
			/*
			 * if (StringUtils.isNotBlank(body)) { body =
			 * URLEncoder.encode(body, "UTF-8"); }
			 */
			HttpEntity se = new StringEntity(body, "UTF-8");
			httpPost.setEntity(se);

			CloseableHttpResponse response = httpclient.execute(httpPost);
			try {
				HttpEntity entity = response.getEntity();

				if (entity != null) {
					BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
					String text;

					while ((text = bufferedReader.readLine()) != null) {
						sb.append(text);
					}

				}
				EntityUtils.consume(entity);
			} finally {
				response.close();
			}
		} finally {
			httpclient.close();
		}
		return sb.toString();
	}

	public static class MySSLProtocolSocketFactory implements ProtocolSocketFactory {
		private SSLContext sslcontext = null;

		public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException, UnknownHostException {
			return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort);
		}

		public Socket createSocket(String host, int port, InetAddress localAddress, int localPort, HttpConnectionParams params)
				throws IOException, UnknownHostException, ConnectTimeoutException {
			if (params == null) {
				throw new IllegalArgumentException("Parameters may not be null");
			}
			int timeout = params.getConnectionTimeout();
			SocketFactory socketfactory = getSSLContext().getSocketFactory();
			if (timeout == 0) {
				return socketfactory.createSocket(host, port, localAddress, localPort);
			} else {
				Socket socket = socketfactory.createSocket();
				SocketAddress localaddr = new InetSocketAddress(localAddress, localPort);
				SocketAddress remoteaddr = new InetSocketAddress(host, port);
				socket.bind(localaddr);
				socket.connect(remoteaddr, timeout);
				return socket;
			}
		}

		public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
			return getSSLContext().getSocketFactory().createSocket(host, port);
		}

		private SSLContext createSSLContext() {
			SSLContext sslcontext = null;
			try {
				sslcontext = SSLContext.getInstance("SSL");
				sslcontext.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom());
			} catch (Exception e) {
				e.printStackTrace();
			}
			return sslcontext;
		}

		private SSLContext getSSLContext() {
			if (this.sslcontext == null) {
				this.sslcontext = createSSLContext();
			}
			return this.sslcontext;
		}

		// 自定义私有类
		private static class TrustAnyTrustManager implements X509TrustManager {

			public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				// TODO Auto-generated method stub

			}

			public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
				// TODO Auto-generated method stub

			}

			public X509Certificate[] getAcceptedIssuers() {
				// TODO Auto-generated method stub
				return null;
			}
		}
	}
}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的微信支付实战代码示例,供您参考: 1. 引入 SDK 等必要文件: ```php require_once "WxPayApi.php"; require_once "WxPayConfig.php"; require_once "WxPayData.php"; require_once "WxPayNotify.php"; ``` 2. 创建支付订单: ```php // 初始化订单信息 $input = new WxPayUnifiedOrder(); $input->SetBody("商品描述"); $input->SetAttach("附加数据"); $input->SetOut_trade_no("商户订单号"); $input->SetTotal_fee("订单总金额,单位为分"); $input->SetTime_start(date("YmdHis")); $input->SetTime_expire(date("YmdHis", time() + 600)); $input->SetGoods_tag("商品标记"); $input->SetNotify_url("回调地址"); $input->SetTrade_type("NATIVE"); $input->SetProduct_id("商品ID"); // 统一下单 $notify = new NativePay(); $result = $notify->GetPayUrl($input); $url = $result["code_url"]; // 微信支付二维码链接地址 ``` 3. 处理支付回调: ```php class PayNotifyCallBack extends WxPayNotify { public function NotifyProcess($data, &$msg) { if (!array_key_exists("transaction_id", $data)) { $msg = "输入参数不正确"; return false; } // 查询订单,判断订单真实性 $order = new WxPayOrderQuery(); $order->SetTransaction_id($data["transaction_id"]); $result = WxPayApi::orderQuery($order); if ($result["return_code"] == "SUCCESS" && $result["result_code"] == "SUCCESS") { // 处理订单逻辑 // ... } return true; } } // 处理支付回调 $notify = new PayNotifyCallBack(); $notify->Handle(true); ``` 需要注意的是,以上代码仅为一个简单的示例,实际开发中还需要根据业务需求进行相应的调整和改进。同时,微信支付的具体实现还需要按照微信支付开发文档中的要求来进行,以确保支付流程的安全性和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值