java集成微信支付(完整流程)
1.申请微信支付能力
* 要想使用微信支付能力,不管是app支付、公众号支付、h5支付等支付方式都需要先在微信商户平台申请开通支付能力。
* 申请开通支付能力的资料有公司营业执照、负责人身份证正反面等图片,相关所需的所有资料在微信官方商户平台上有说明。
* 申请完开通支付能力后,我们会得到商户号以及appId,然后设置32位官方密钥。
2.准备工作
* 如果你是h5支付,还需要去微信商户平台设置支付URL的IP或者域名,一般最多可以设置5个IP或者域名,建议同时将正式环境和测试环境的IP或者域名设置好。
* 如果你是公众号支付,同上,你也需要设置你的支付IP或者域名,注意,异步通知的URL也要在你设置的IP或者域名下。
3.开始集成
*** APP支付**
支付集成流程如下:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。(app端向服务端发起请求)
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。(服务端向微信请求)
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式为Sign=WXPay(将微信回传的prepayid与其他参数组合返回给app端)
步骤4:商户APP调起微信支付。(app端利用服务端回传的参数调起微信支付)
步骤5:商户后台接收支付通知。(微信将支付结果异步通知服务端)
步骤6:商户后台查询支付结果。(微信将支付结果同步通知app端)
上代码:
DecimalFormat df = new DecimalFormat("######0.00");
//根据订单id查询出该订单的金额
money=df.format(Double.parseDouble(okamiOrder.getReallMoney()));
//定义返回值
ReturnValue rv=new ReturnValue();
String token=request.getParameter("token");
//当前用户的id
String userId=TokenParmValue.getUserId(token);
//开始组装调用统一下单接口所需的参数
String currTime = TenpayUtil.getCurrTime();
// 8位日期
String strTime = currTime.substring(8, currTime.length());
// 四位随机数
String strRandom = TenpayUtil.buildRandom(4) + "";
// 10位序列号,可以自行调整。
String strReq =strTime+strRandom;
SONObject retMsgJson = new JSONObject();
//应用配置
String appId=""; //appid
String appSecret=""; //微信支付appsecret
String partner=""; //微信商户号
String partnerkey=""; //api密钥
//定义应用支付配置
appId=WeixinPayUtil.appid;
appSecret=WeixinPayUtil.appsecret;
partner=WeixinPayUtil.partner;
partnerkey=WeixinPayUtil.partnerkey;
//********************************************当前微信支付业务处理开始
// 时间戳加后四位随机数作为商户订单号
String sjbh = RandomUtil.getRandomFileName();
// 封装参数
TreeMap treeMap = new TreeMap();
// 微信支付所需要的appId
treeMap.put("appid",appId);
// 微信商户号
treeMap.put("mch_id",partner);
// 随机数
treeMap.put("nonce_str", strReq);
// 订单内容
treeMap.put("body",subject);
// 商户订单号
treeMap.put("out_trade_no", sjbh);
// 终端ip(下单生成机器的ip),可以获取支付接口的请求IP
treeMap.put("spbill_create_ip", Property.getProperty("weixinIp"));
// 订单金额
treeMap.put("total_fee",money);
// 支付类型
treeMap.put("trade_type",WeixinPayUtil.trade_type);
// 微信异步通知地址
treeMap.put("notify_url",Property.getProperty("okamiWeixinUrl").trim());
//禁止充值的时候使用信用卡
if("3".equals(payType)){
treeMap.put("limit_pay","no_credit");
}
StringBuilder sb = new StringBuilder();
for (String key : treeMap.keySet()) {
sb.append(key).append("=").append(treeMap.get(key)).append("&");
}
sb.append("key="+partnerkey);
// 获得微信支付验签
RequestHandler reqHandler = new RequestHandler(request, response);
reqHandler.init(appId,appSecret,partnerkey);
// 参数加签名认证
String sign = reqHandler.createSign(treeMap);// 获取签名
treeMap.put("sign", sign);
StringBuilder xml = new StringBuilder();
xml.append("\n");
for (Map.Entry entry : treeMap.entrySet()) {
if ("body".equals(entry.getKey()) || "sign".equals(entry.getKey())) {
xml.append("<" + entry.getKey() + ">" + entry.getKey() + ">\n");
} else {
xml.append("<" + entry.getKey() + ">").append(entry.getValue()).append("" + entry.getKey() + ">\n");
}
}
xml.append(" ");
//开始调用统一下单接口
String createOrderURL = WeixinPayUtil.createOrderURL;
String prepay_id = "";
try {
// 获得预下单的订单号
prepay_id = GetWxOrderno.getPayNo(createOrderURL,xml.toString());
} catch (Exception e1) {
e1.printStackTrace();
rv.setResult("error");
rv.setMsg(ReturnUtil.PAYMENT_FAILURE);
return new JsonMapper().toJson(rv);
}
log.info("微信支付prepay_id的值为:----------------"+prepay_id);
// 获取到prepayid后对以下字段进行签名最终发送给app
SortedMap finalpackage = new TreeMap();
String timestamp = Sha1Util.getTimeStamp();
finalpackage.put("appid",appId);
finalpackage.put("noncestr", strReq);
finalpackage.put("partnerid", partner);
finalpackage.put("timestamp", timestamp);
finalpackage.put("package", "Sign=WXPay");
finalpackage.put("prepayid", prepay_id);
String wenXinSign=WenXinPay.createSign("UTF-8",finalpackage,partnerkey);
retMsgJson.put("appid",appId);
retMsgJson.put("timestamp",timestamp);
retMsgJson.put("noncestr", strReq);
retMsgJson.put("partnerid",partner);
retMsgJson.put("prepayid", prepay_id);
retMsgJson.put("packageX", "Sign=WXPay");
retMsgJson.put("sign", wenXinSign);
// 最终给app的参数
String json = retMsgJson.toString();
以上是app端发起微信支付,服务端回传app端所需要的参数。
异步通知处理:
在做异步通知功能之前一定要设计好异步通知功能,防止重复通知,建议通知成功后将记录储存与缓存或者数据库中,以避免重复异步通知。
@RequestMapping(value = "payment/wechatAsynchronous",method=RequestMethod.POST,produces = "text/html;charset=UTF-8")
@ResponseBody
public void wechatAsynchronous(HttpServletRequest requestWechat,HttpServletResponse response) throws Exception{
log.info("进入微信异步通知啦"+moduleName+"方法名为:wechatAsynchronous");
DecimalFormat df = new DecimalFormat("######0.00");
DecimalFormat dformat = new DecimalFormat("######0");
requestWechat.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.setHeader("Access-Control-Allow-Origin", "*");
InputStream in = requestWechat.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = 0;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
out.close();
in.close();
String msgxml = new String(out.toByteArray(), "utf-8");// xml数据
System.out.println(msgxml);
log.info("msgxml的值为:-----------------"+msgxml);
Map map = XmlToMap.xmlToMap(msgxml);
//过滤空 设置 TreeMap
SortedMap packageParams = new TreeMap();
Iterator it = map.keySet().iterator();
while (it.hasNext()) {
String parameter = (String) it.next();
String parameterValue = (String) map .get(parameter);
String v = "";
if(null != parameterValue) {
v = parameterValue.trim();
}
packageParams.put(parameter, v);
}
log.info("进入微信异步通知啦"+moduleName+"异步通知参数:"+packageParams);
// 获得支付结果
String result_code = (String) map.get("result_code");
// 获得商户订单号
String out_trade_no = (String) map.get("out_trade_no");
// 获得订单签名
String sign = (String) map.get("sign");
log.info("sign订单签名为:---------------------"+sign);
//买家实际支付金额
String total_fee=(String)map.get("total_fee");
String realMoney=String.valueOf(Double.parseDouble(total_fee)/100);
// 交易记录表的id
String tranId = out_trade_no.replace(" ", "");
log.info("参数 tranId:" + tranId);
log.info("微信支付的result_code为-----------------------:" + result_code);
// 根据交易id查询一条交易记录
Transaction tran = tranService.findById(tranId);
if(tran!=null){
log.info("tran对象的值不为空:--------------------");
// 订单表的id
String orderId = tran.getOrderId();
log.info("参数订单主键 orderId:" + orderId);
// 微信支付成功
if (result_code.equals("SUCCESS")){
//防止重复通知
long size=requestRedisTemplate.boundValueOps("weixinpay:lock:"+out_trade_no).increment(1);
requestRedisTemplate.expire("weixinpay:lock:"+out_trade_no,24,TimeUnit.HOURS);
if(size==1){
log.info("进入微信异步通知啦"+moduleName+"异步通知请求进入!!!");
//插入异步通知日志
asyncService.insertResrcord("weixin",tran.getUserId(),(String)map.get("transaction_id"),realMoney,tranId);
// 财付通订单号
String transaction_id = (String) map.get("transaction_id");
//获得微支付商户号
String mch_id=(String) map.get("mch_id");
//获得微信支付appid---->异步通知
String appid=(String) map.get("appid");
if(StringUtils.isNotBlank(mch_id)&&StringUtils.isNotBlank(appid)){
log.info("商户号与appid不为空");
//根据不同的appid校验应用配置
//应用1
String appId_local_one=WeixinPayUtil.appid;
//应用2
String appId_local_two=WeixinPayUtil.appid_two;
//应用3
String appId_local_three=WeixinPayUtil.appid_three;
String partner_local=WeixinPayUtil.partner;
String partnerKey_local=WeixinPayUtil.partnerkey;
String appVersion="1";
// 验证商户号是否正确
if (mch_id.equals(partner_local)) {
log.info("微信商户号与appid验证验证成功");
//验证签名
if(WenXinPay.isTenpaySign("UTF-8", packageParams,partnerKey_local)){
log.info("微信验证签名成功了!!!!!!");
// 业务处理
}else{
response.getWriter().write(setXml("FAIL", "ERROR"));
log.info("微信支付异步通知失败了!!!!!!");
}
}else{
response.getWriter().write(setXml("FAIL", "ERROR"));
log.info("微信支付异步通知失败了!!!!!!");
}
}
//微信异步通知返回xml格式数据
public static String setXml(String return_code, String return_msg) {
return " ";
}
以上就是app支付的所有代码,其中所用的工具类大部分为微信支付开发文档中demo提供,下载地址为:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_3
***h5支付**
记得在微信官方商户平台配置好支付域名
基本流程:
1、用户在商户侧完成下单,使用微信支付进行支付。
2、由商户后台向微信支付发起下单请求(调用统一下单接口)注:交易类型trade_type=MWEB。
3、统一下单接口返回支付相关参数给商户后台,如支付跳转url(参数名“mweb_url”),商户通过mweb_url调起微信支付中间页。
4、中间页进行H5权限的校验,安全性检查。
5、如支付成功,商户后台会接收到微信侧的异步通知。
6、用户在微信支付收银台完成支付或取消支付,返回商户页面。
7、商户在展示页面,引导用户主动发起支付结果的查询。
8,9、商户后台判断是否接到收微信侧的支付结果通知,如没有,后台调用我们的订单查询接口确认订单状态。
10、展示最终的订单支付结果给用户。
**此处注意微信h5支付最终的结果是提供前端一个支付链接的字符串。**
支付接口代码:
/**
* 微信发送支付请求,微信官方h5支付
* @param paramMap
* @return
*/
@Override
public Result officialSend(Map paramMap){
Result result=new Result();
String pay_url="";
// 封装参数
TreeMap treeMap = new TreeMap();
// 微信支付所需要的appId
treeMap.put("appid",WeiXinConstant.official_app_id);
// 微信商户号
treeMap.put("mch_id",WeiXinConstant.official_mch_id);
// 随机数
treeMap.put("nonce_str",TenpayUtil.getNonceStr());
// 订单内容
treeMap.put("body",paramMap.get("productname")+"");
// 商户订单号
treeMap.put("out_trade_no",paramMap.get("orderId")+"");
// 终端ip(下单生成机器的ip)
treeMap.put("spbill_create_ip",paramMap.get("spbill_create_ip")+"");
// 订单金额
treeMap.put("total_fee",new Double(Double.valueOf(paramMap.get("money")+"")*100).intValue()+"");
// 支付类型
treeMap.put("trade_type","MWEB");//h5支付
// 微信异步通知地址
treeMap.put("notify_url", PropertiesUtil.getDom()+WeiXinConstant.official_notify_url);
//业务参数
treeMap.put("attach",paramMap.get("orderId")+"");
JSONObject js=new JSONObject();
JSONObject jb=new JSONObject();
if("1".equals(paramMap.get("source"))){//安卓
js.put("type", "Android");
js.put("app_name", "8wan游戏");
js.put("package_name", "com.xianqing.sdk");
jb.put("h5_info", js);
}else{//ios
js.put("type", "IOS");
js.put("bundle_id", "com.xianqing.sdk");
js.put("app_name", "8wan游戏");
jb.put("h5_info", js);
}
//场景信息
treeMap.put("scene_info",jb.toString());
//禁止充值的时候使用信用卡,默认可以使用
//treeMap.put("limit_pay","no_credit");
Map params = SignUtils.paraFilter(treeMap);
StringBuilder buf = new StringBuilder((params.size() +1) * 10);
SignUtils.buildPayParams(buf,params,false);
String preStr = buf.toString();
String sign = MD5.sign(preStr, "&key=" + WeiXinConstant.partnerkey, "utf-8");
treeMap.put("sign", sign);
CloseableHttpResponse response = null;
CloseableHttpClient client = null;
try {
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/pay/unifiedorder");
StringEntity entityParams = new StringEntity(WXPayUtil.mapToXml(treeMap),"utf-8");
httpPost.setEntity(entityParams);
//httpPost.setHeader("Content-Type", "text/xml;charset=ISO-8859-1");
client = HttpClients.createDefault();
response = client.execute(httpPost);
if(response != null && response.getEntity() != null){
Map resultMap = WXPayUtil.xmlToMap(new String(EntityUtils.toByteArray(response.getEntity())));
if("SUCCESS".equalsIgnoreCase(resultMap.get("return_code"))){
if("SUCCESS".equalsIgnoreCase(resultMap.get("result_code"))){
String prepay_id=resultMap.get("prepay_id");
String mweb_url=resultMap.get("mweb_url");
logger.info("微信官方支付mweb_url:"+mweb_url);
String packagevalue="";
if(mweb_url.indexOf("&package=")!=-1){
packagevalue=mweb_url.substring(mweb_url.indexOf("&package=")+9,mweb_url.length());
}
String type="";
if(PropertiesUtil.getDom().indexOf("test")!=-1){
type="1";
}else{
type="2";
}
pay_url=PropertiesUtil.getDom()+"pay/pay_h5_wx.html?prepay_id="+prepay_id+"&packagevalue="+packagevalue+"&type="+type;
}
}else{
logger.info("微信官方h5支付失败,失败原因为:"+resultMap.get("return_msg"));
}
logger.info("支付结果为:"+resultMap.toString());
}else{
logger.info("微信官方h5支付失败,请求未响应");
}
} catch (Exception e) {
logger.info("微信官方支付错误信息:"+e.getMessage());
} finally {
if(response != null){
try {
response.close();
} catch (IOException e) {
logger.info("微信官方支付,响应关闭错误信息:"+e.getMessage());
}
}
if(client != null){
try {
client.close();
} catch (IOException e) {
logger.info("微信官方支付,请求关闭错误信息:"+e.getMessage());
}
}
}
JSONObject job=new JSONObject();
if(StringUtils.isNotBlank(pay_url)){
job.put("res",pay_url);
}else{
job.put("res","");
}
result.setData(job);
return result;
}
注意,微信h5支付接口最终返回给前端是一个支付链接,此链接所在的页面的IP或者域名必须要在你之前在商户平台配置的IP或者域名之下,另外如果是app请求h5支付接口,也同样将此链接返回给app端,最后贴上支付连接中页面的代码:
微信官方h5支付
异步通知代码:
/**
* 微信支付回调,微信官方h5支付
* @return
*/
@Override
public Result callbackUrlOfficial(HttpServletRequest req) throws Exception{
String resString = XmlUtils.parseRequst(req);
logger.info("微信官方h5支付回调参数:"+resString);
Result result = new Result();
if(StringUtils.isNotBlank(resString)){
Map map = WXPayUtil.xmlToMap(resString);
logger.info("微信官方h5支付通知map内容:"+map);
String return_code = map.get("return_code");
logger.info("微信官方h5支付异步通知业务结果为:"+return_code);
if("SUCCESS".equals(return_code)){//通知状态 SUCCESS:成功
if(map.containsKey("sign")){
if(SignUtils.checkParam(map, WeiXinConstant.partnerkey)){//验证签名通过
logger.info("微信官方h5支付异步通知签名通过!!!sign-success");
if("SUCCESS".equals(map.get("result_code"))){//支付结果 SUCCESS:成功
//业务处理
//判断微信支付返回值表中是否存在记录,如果不存在则插入返回信息
List list =payServiceImpl.getPayResponse(map.get("out_trade_no"));
if(list == null || list.size() == 0){
payServiceImpl.addPayResponse(map.get("out_trade_no"), map.get("transaction_id"), "5", map.toString());
//开始业务处理
}
return result;
}else{
logger.info("微信官方h5订单支付失败:平台订单编号orderid="+map.get("attach"));
result.setCode(SystemStatus.WX_ORDER_PAY_ERROR.CODE);
result.setMsg(SystemStatus.WX_ORDER_PAY_ERROR.MSG);
return result;
}
}else{
logger.info("微信官方h5支付回调签名错误:平台订单编号orderid="+map.get("attach"));
result.setCode(SystemStatus.WX_SIGN_ERR.CODE);
result.setMsg(SystemStatus.WX_SIGN_ERR.MSG+"签名错误");
return result;
}
}else{
logger.info("微信官方h5支付回调签名不存在:平台订单编号orderid="+map.get("attach"));
result.setCode(SystemStatus.WX_SIGN_ERR.CODE);
result.setMsg(SystemStatus.WX_SIGN_ERR.MSG+"签名不存在");
return result;
}
}else{
logger.info("微信官方h5支付异步通知失败,失败原因为"+map.get("return_msg"));
result.setCode(SystemStatus.PARAM_ERROR.CODE);
result.setMsg(SystemStatus.PARAM_ERROR.MSG);
return result;
}
}else{
result.setCode(SystemStatus.PARAM_ERROR.CODE);
result.setMsg(SystemStatus.PARAM_ERROR.MSG);
return result;
}
}
以上就是微信h5支付的所有代码,其中所涉及到的工具类请大家自己在官方开发文档上下载:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4
*公众号支付*
公众号支付与h5支付类似,只需要将支付方式改成公众号支付方式就行,参考文档为:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3
同样,公众号支付也是需要在你的公众号后台设置授权域名。