java实现微信h5支付详解编程语言

微信h5支付需要在微信商户平台-》产品中心开通h5支付。
这里写图片描述
官网提供的开发文档中需要的参数:
这里写图片描述这里写图片描述这里写图片描述
h5支付主要是scene_info中的参数wap_url必须是可以访问到的地址。spbill_create_ip的获取必须和调起微信支付的ip一致。
代码实现如下:
action中代码:

private static DecimalFormat df = new DecimalFormat("0.00"); 
@Action("weChatUnifiedorder") 
public String weChatUnifiedorder(){ 
try { 
df.setRoundingMode(RoundingMode.HALF_UP); 
// 获取用户ID 
String memberId = Struts2Utils.getRequest().getParameter("memberId"); 
String integer = Struts2Utils.getRequest().getParameter("integer"); 
String type = Struts2Utils.getRequest().getParameter("type"); 
//我这里的ip直接从前端页面获取部分手机不能获取 
String spbill_create_ip = Struts2Utils.getRequest().getParameter("ip"); 
if (StringUtils.isNotBlank(memberId) 
&& StringUtils.isNotBlank(integer) 
&& StringUtils.isNotBlank(type)) { 
Integer score = Integer.valueOf(integer); 
// 产生订单号 
String outTradeNo = UuIdUtils.getUUID(); 
// WeixinConfigUtils config = new WeixinConfigUtils(); 
// 参数组 
String appid = config.appid; 
String mch_id = config.mch_id; 
String nonce_str = RandCharsUtils.getRandomString(16); 
String body = "";    
if (type.equals("0")) { 
body = "购买" + score + "积分,支付" + df.format(score / 100) 
+ "元"; 
} else if (type.equals("1")) { 
body = "购买商品支付" + String.valueOf(score / 100) + "元"; 
} 
String attach = "暂时无用(这个用于区分不同版本)"; 
String out_trade_no = outTradeNo; 
int total_fee = score;// 单位是分,现在按照ios传递过来的参数进行 
String notify_url = config.notify_url; 
String trade_type = "MWEB"; 
// 参数:开始生成第一次签名 
parameters.put("appid", appid); 
parameters.put("mch_id", mch_id); 
parameters.put("body", body); 
parameters.put("nonce_str", nonce_str); 
parameters.put("attach", attach); 
parameters.put("out_trade_no", out_trade_no); 
parameters.put("total_fee", total_fee); 
parameters.put("notify_url", notify_url); 
parameters.put("trade_type", trade_type); 
parameters.put("spbill_create_ip", spbill_create_ip); 
parameters.put("scene_info", "'h5_info':{'type':'Wap','wap_url':'www.abc.com(必须可以直接访问,**这里不要带http://**)','wap_name': '应用名(会在微信订单页显示)'}"); 
String sign = WXSignUtils.createSign("UTF-8", parameters); 
// 微信统一下单 
unifiedorder.setAppid(appid); 
unifiedorder.setMch_id(mch_id); 
unifiedorder.setNonce_str(nonce_str); 
unifiedorder.setSign(sign); 
unifiedorder.setBody(body); 
unifiedorder.setAttach(attach); 
unifiedorder.setOut_trade_no(out_trade_no); 
unifiedorder.setTotal_fee(total_fee); 
unifiedorder.setSpbill_create_ip(spbill_create_ip); 
unifiedorder.setNotify_url(notify_url); 
unifiedorder.setTrade_type(trade_type); 
unifiedorder.setScene_info("'h5_info':{'type':'Wap','wap_url':'http://www.abc.com(必须可以直接访问**这里不要带http://**)','wap_name': '应用名(会在微信订单页显示)'}"); 
Map<String, String> msgMap = HttpXmlUtils.getUrl(unifiedorder); 
if (msgMap.get("return_code").equals("SUCCESS") && msgMap.get("result_code").equals("SUCCESS")) { 
aLiPay.setApp_id(appid); 
aLiPay.setSubject(body); 
aLiPay.setScore(integer); 
aLiPay.setGmt_create(simpleDateFormat.format(new Date())); 
if (null != memberId && !"".equals(memberId)) { 
aLiPay.setMember(memberService.getMemberById(Integer 
.valueOf(memberId))); 
} 
aLiPay.setOut_trade_no(out_trade_no); 
aLiPay.setPayStatus(PayStatusEnum.WAITPAY.getComment()); 
aLiPay.setPayType(PayTypeEnum.WEIXIN.getComment()); 
aLiPay.setState(0); 
aLiPay.setTotal_amount(String.valueOf(total_fee / 100)); 
aLiPay.setSign(sign); 
aLiPay.setTotalFee(total_fee); 
aLiPayService.addALiPay(aLiPay); 
msg.put("ok", true); 
//将回调地址发回前端,支付完成后会自动跳转该页面(回调url必须带http://) 
msg.put("url", msgMap.get("mweb_url")+"&redirect_url=http://www.jabc.com/member/toMemberIntegral.do"); 
Struts2Utils.renderJson(msg); 
}else { 
msg.put("ok", false); 
} 
} 
return NONE; 
} catch (Exception e) { 
log.error(e.getMessage()); 
return "500"; 
} 
}

微信支付签名代码:

public class WXSignUtils { 
/** 
* 微信支付签名算法sign 
* @param characterEncoding 
* @param parameters 
* @return 
*/ 
@SuppressWarnings("rawtypes") 
public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){ 
StringBuffer sb = new StringBuffer(); 
Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序) 
Iterator it = es.iterator(); 
while(it.hasNext()) { 
Map.Entry entry = (Map.Entry)it.next(); 
String k = (String)entry.getKey(); 
Object v = entry.getValue(); 
if(null != v && !"".equals(v)  
&& !"sign".equals(k) && !"key".equals(k)) { 
sb.append(k + "=" + v + "&"); 
} 
} 
sb.append("key=" + weixinConstant.KEY); 
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); 
return sign; 
} 
}

向微信发送post数据并解析返回的微信订单号

public class HttpXmlUtils { 
/** 
* h5支付时 解析返回的值并返回prepareid 
* @throws IOException  
* @throws JDOMException  
*/ 
public static Map<String, String> getUrl(Unifiedorder unifiedorder) throws JDOMException, IOException{ 
//构造xml 
String xmlInfo = HttpXmlUtils.xmlH5Info(unifiedorder); 
String wxUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder"; 
String method = "POST"; 
//获取微信返回的结果 
String weixinPost = HttpXmlUtils.httpsRequest(wxUrl, method, xmlInfo).toString(); 
//解析返回的xml 
ParseXMLUtils.jdomParseXml(weixinPost); 
StringReader read = new StringReader(weixinPost); 
// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入 
InputSource source = new InputSource(read); 
// 创建一个新的SAXBuilder 
SAXBuilder sb = new SAXBuilder(); 
// 通过输入源构造一个Document 
org.jdom.Document doc; 
doc = (org.jdom.Document) sb.build(source); 
org.jdom.Element root = doc.getRootElement();// 指向根节点 
List<org.jdom.Element> list = root.getChildren(); 
String prepayId =null; 
Map<String, String> msg = new HashMap<String, String>(); 
if(list!=null&&list.size()>0){ 
for (org.jdom.Element element : list) { 
msg.put(element.getName(), element.getText()); 
} 
} 
return msg; 
} 
/** 
* 构造xml参数 
* @param xml 
* @return 
*/ 
public static String xmlH5Info(Unifiedorder unifiedorder){ 
if(unifiedorder!=null){ 
StringBuffer bf = new StringBuffer(); 
bf.append("<xml>"); 
bf.append("<appid><![CDATA["); 
bf.append(unifiedorder.getAppid()); 
bf.append("]]></appid>"); 
bf.append("<mch_id><![CDATA["); 
bf.append(unifiedorder.getMch_id()); 
bf.append("]]></mch_id>"); 
bf.append("<nonce_str><![CDATA["); 
bf.append(unifiedorder.getNonce_str()); 
bf.append("]]></nonce_str>"); 
bf.append("<sign><![CDATA["); 
bf.append(unifiedorder.getSign()); 
bf.append("]]></sign>"); 
bf.append("<body><![CDATA["); 
bf.append(unifiedorder.getBody()); 
bf.append("]]></body>"); 
bf.append("<attach><![CDATA["); 
bf.append(unifiedorder.getAttach()); 
bf.append("]]></attach>"); 
bf.append("<out_trade_no><![CDATA["); 
bf.append(unifiedorder.getOut_trade_no()); 
bf.append("]]></out_trade_no>"); 
bf.append("<total_fee><![CDATA["); 
bf.append(unifiedorder.getTotal_fee()); 
bf.append("]]></total_fee>"); 
bf.append("<spbill_create_ip><![CDATA["); 
bf.append(unifiedorder.getSpbill_create_ip()); 
bf.append("]]></spbill_create_ip>"); 
bf.append("<notify_url><![CDATA["); 
bf.append(unifiedorder.getNotify_url()); 
bf.append("]]></notify_url>"); 
bf.append("<trade_type><![CDATA["); 
bf.append(unifiedorder.getTrade_type()); 
bf.append("]]></trade_type>"); 
bf.append("<scene_info><![CDATA["); 
bf.append(unifiedorder.getScene_info()); 
bf.append("]]></scene_info>"); 
bf.append("</xml>"); 
return bf.toString(); 
} 
return ""; 
} 
/** 
* post请求并得到返回结果 
* @param requestUrl 
* @param requestMethod 
* @param output 
* @return 
*/ 
public static String httpsRequest(String requestUrl, String requestMethod, String output) { 
try{ 
URL url = new URL(requestUrl); 
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); 
connection.setDoOutput(true); 
connection.setDoInput(true); 
connection.setUseCaches(false); 
connection.setRequestMethod(requestMethod); 
if (null != output) { 
OutputStream outputStream = connection.getOutputStream(); 
outputStream.write(output.getBytes("UTF-8")); 
outputStream.close(); 
} 
// 从输入流读取返回内容 
InputStream inputStream = connection.getInputStream(); 
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); 
BufferedReader bufferedReader = new BufferedReader(inputStreamReader); 
String str = null; 
StringBuffer buffer = new StringBuffer(); 
while ((str = bufferedReader.readLine()) != null) { 
buffer.append(str); 
} 
bufferedReader.close(); 
inputStreamReader.close(); 
inputStream.close(); 
inputStream = null; 
connection.disconnect(); 
return buffer.toString(); 
}catch(Exception ex){ 
ex.printStackTrace(); 
} 
return ""; 
} 
}

解析微信返回的xml

/** 
* 3、JDOM解析XML 
* 解析的时候自动去掉CDMA 
* @param xml 
*/ 
@SuppressWarnings("unchecked") 
public static void jdomParseXml(String xml){ 
try {  
StringReader read = new StringReader(xml); 
// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入 
InputSource source = new InputSource(read); 
// 创建一个新的SAXBuilder 
SAXBuilder sb = new SAXBuilder(); 
// 通过输入源构造一个Document 
org.jdom.Document doc; 
doc = (org.jdom.Document) sb.build(source); 
org.jdom.Element root = doc.getRootElement();// 指向根节点 
List<org.jdom.Element> list = root.getChildren(); 
if(list!=null&&list.size()>0){ 
for (org.jdom.Element element : list) { 
} 
} 
} catch (JDOMException e) { 
e.printStackTrace(); 
}  catch (IOException e) { 
e.printStackTrace(); 
}catch (Exception e) { 
e.printStackTrace(); 
} 
}

封装支付参数的实体类:(参数名与官网参数名一致,此类封装了h5、app支付和退款的参数)

import java.io.Serializable; 
/** 
* 统一下单提交为微信的参数 
* @author  
* @date 2017年08月11日 
*/ 
public class Unifiedorder implements Serializable{ 
private static final long serialVersionUID = 1L; 
//微信支付表id 
private Integer weixinId; 
//微信分配的公众账号ID(企业号corpid即为此appId) 
private String appid; 
//商户id 
private String mch_id; 
//终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB" 
private String device_info; 
//随机字符串:数字+大写字母的组合,32位 
private String nonce_str; 
//签名 
private String sign; 
//商品或支付单简要描述 
private String body; 
//商品名称明细列表 
private String detail; 
//附加参数(例如:用于区别本商户不同的分店) 
private String attach; 
//商户系统内部的订单号 
private String out_trade_no; 
//货币类型:符合ISO 4217标准的三位字母代码,默认人民币:CNY 
private String fee_type; 
//总金额 
private int total_fee; 
//APP和网页支付提交[用户端ip],Native支付填调用微信支付API的机器IP。 
private String spbill_create_ip; 
//订单生成时间,格式为yyyyMMddHHmmss, 
private String time_start; 
//订单失效时间,格式为yyyyMMddHHmmss,最短失效时间间隔必须大于5分钟[支付宝是30分钟,同样30分钟] 
private String time_expire; 
//商品标记,代金券或立减优惠功能的参数 
private String goods_tag; 
//接收微信支付异步通知回调地址 
private String notify_url; 
//交易类型:JSAPI,NATIVE,APP h5为 MWEB 
private String trade_type; 
//trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。 
private String product_id; 
//no_credit--指定不能使用信用卡支付 
private String limit_pay; 
//trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识 
private String openid; 
//商户内部自己的退款单号 
private String out_refund_no; 
//退款总金额单位为分 
private int refund_fee; 
//操作员的id默认为mch_id 
private String op_user_id; 
//微信官方提供的订单号 
private String prepayid; 
//记录所对应的member 
private Member member; 
//返回给微信的状态码(用于支付回调时) 
public String return_code; 
//微信h5支付时候的场景信息官方的信息模板 {"h5_info"://h5支付固定传"h5_info"  
//{"type":"",//场景类型 "wap_url":"",//WAP网站URL地址"wap_name": ""//WAP 网站名}} 
public String scene_info; 
public String getScene_info() { 
return scene_info; 
} 
public void setScene_info(String scene_info) { 
this.scene_info = scene_info; 
} 
public String getReturn_code() { 
return return_code; 
} 
public void setReturn_code(String return_code) { 
this.return_code = return_code; 
} 
public String getAppid() { 
return appid; 
} 
public String getMch_id() { 
return mch_id; 
} 
public String getDevice_info() { 
return device_info; 
} 
public String getNonce_str() { 
return nonce_str; 
} 
public String getSign() { 
return sign; 
} 
public String getBody() { 
return body; 
} 
public String getDetail() { 
return detail; 
} 
public String getAttach() { 
return attach; 
} 
public String getOut_trade_no() { 
return out_trade_no; 
} 
public String getFee_type() { 
return fee_type; 
} 
public int getTotal_fee() { 
return total_fee; 
} 
public String getSpbill_create_ip() { 
return spbill_create_ip; 
} 
public String getTime_start() { 
return time_start; 
} 
public String getTime_expire() { 
return time_expire; 
} 
public String getGoods_tag() { 
return goods_tag; 
} 
public String getNotify_url() { 
return notify_url; 
} 
public String getTrade_type() { 
return trade_type; 
} 
public String getProduct_id() { 
return product_id; 
} 
public String getLimit_pay() { 
return limit_pay; 
} 
public String getOpenid() { 
return openid; 
} 
public void setAppid(String appid) { 
this.appid = appid; 
} 
public void setMch_id(String mch_id) { 
this.mch_id = mch_id; 
} 
public void setDevice_info(String device_info) { 
this.device_info = device_info; 
} 
public void setNonce_str(String nonce_str) { 
this.nonce_str = nonce_str; 
} 
public void setSign(String sign) { 
this.sign = sign; 
} 
public void setBody(String body) { 
this.body = body; 
} 
public void setDetail(String detail) { 
this.detail = detail; 
} 
public void setAttach(String attach) { 
this.attach = attach; 
} 
public void setOut_trade_no(String out_trade_no) { 
this.out_trade_no = out_trade_no; 
} 
public void setFee_type(String fee_type) { 
this.fee_type = fee_type; 
} 
public void setTotal_fee(int total_fee) { 
this.total_fee = total_fee; 
} 
public void setSpbill_create_ip(String spbill_create_ip) { 
this.spbill_create_ip = spbill_create_ip; 
} 
public void setTime_start(String time_start) { 
this.time_start = time_start; 
} 
public void setTime_expire(String time_expire) { 
this.time_expire = time_expire; 
} 
public void setGoods_tag(String goods_tag) { 
this.goods_tag = goods_tag; 
} 
public void setNotify_url(String notify_url) { 
this.notify_url = notify_url; 
} 
public void setTrade_type(String trade_type) { 
this.trade_type = trade_type; 
} 
public void setProduct_id(String product_id) { 
this.product_id = product_id; 
} 
public void setLimit_pay(String limit_pay) { 
this.limit_pay = limit_pay; 
} 
public void setOpenid(String openid) { 
this.openid = openid; 
} 
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 int getRefund_fee() { 
return refund_fee; 
} 
public void setRefund_fee(int refund_fee) { 
this.refund_fee = refund_fee; 
} 
public Integer getWeixinId() { 
return weixinId; 
} 
public void setWeixinId(Integer weixinId) { 
this.weixinId = weixinId; 
} 
public Member getMember() { 
return member; 
} 
public void setMember(Member member) { 
this.member = member; 
} 
public String getPrepayid() { 
return prepayid; 
} 
public void setPrepayid(String prepayid) { 
this.prepayid = prepayid; 
} 
public String getOp_user_id() { 
return op_user_id; 
} 
public void setOp_user_id(String op_user_id) { 
this.op_user_id = op_user_id; 
} 
} 

前端获取ip:

<script src="http://pv.sohu.com/cityjson?ie=utf-8"> 
var ip = returnCitySN.cip;(有些手机不行,目前发现的是魅族和三星)判断正不正确可以查自己的ip地址,微信获取的是你所处网络的ip。也可以在java后台获取 
</script> 

在h5支付时出现的一些问题
这个
这个页面就是你传递的ip和微信获取的发起支付的ip不一致,这个时候你就要检查获取ip的代码
这里写图片描述
出现这个情况的原因有两个1.你的scene_info中的wap_url与你配置的授权域名不一致,需要去商户平台的产品中心->开发配置中修改 2. 配置的redirect_url与授权域名不一致,需要修改。
这个地方还有其他的一些错误,我没遇到,以上两个错误我遇到很多次,后来查看官方文档才看见的(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4)。

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/13976.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论