功能简介
微信支付,是基于微信客户端提供的支付服务功能。同时向商户提供销售经营分析、 账户和资金管理的技术支持。 用户通过扫描二维码、 点击图文消息进入商品页面购买等多种方式调起微信支付模块完成支付。
目前微信支持公众号内支付。 其中支付方式, 可以分为 JS API 支付、 Native(原生)支付。商户可以结合业务场景,自主选择支付形式。
公众号支付有 2 种方式:
1. JS API 支付:是指用户打开图文消息或者扫描二维码,在微信内置浏览器 打开网页进行的支付。 商户网页前端通过使用微信提供的 JS API, 调用微信支付模块。 这种方式, 适合需要在商户网页进行选购下单的购买流程。
2. Native(原生)支付:是指商户组成符合 Native(原生)支付规则的 URL 链接,用户可通过点击该链接或者扫描对应的二维码直接进入微信支付模块(微信客户端界面) ,即可进行支付。这种方式,适合无需选购直接支付的购买流程。
以上两种支付方式, 最大的差别在于是否需要经过网页调起支付。 下面我们要讲的是基于微信内置浏览器的JS API 支付:
JS API支付接口(getBrandWCPayRequest)
微信JS API只能在微信内置浏览器中使用,其他浏览器调用无效。微信提供getBrandWCPayRequest接口供商户前端网页调用,调用之前微信会鉴定商户支付权限,若商户具有调起支付的权限,则将开始支付流程。(接口需要注意:所有传入参数都是字符串类型!)
首先,要明确微信支付开发的一些准备工作,参考腾讯帮助文档:http://kf.qq.com/faq/120911VrYVrA150905zeYjMZ.html
1. 注册微信公众号(注意必须是服务号),并通过微信认证(认证费:300元/次)。
微信认证审核时间约7个工作日左右
2. 登录公众平台,提交资料申请微信支付(资料审核时间为3~7个工作日)。申请成功后,就可以进行支付接口的设计和开发了。参考文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_1
3.开户成功,登录商户平台进行验证,在线签署协议(平台帐户和密码请查看收到的开户邮件,验证款项(随机金额)请查收你的结算帐户)。平台账户默认是商户号@商户号,比如你的商户号是1226265502,那么你的商户平台帐号默认是:1226265502@1226265502。
成功接入微信支付后,我们着手开发支付接口。需要用到的相关参数如下:
/**
* 服务号相关信息
*/
public final static String APPID = "";//服务号的应用号,已wx开头
public final static String APP_SECRECT = "";//服务号的应用密码
public final static String MCH_ID = "";//商户号
public final static String API_KEY = "";//API密钥
public final static String SIGN_TYPE = "MD5";//签名加密方式
//微信支付统一接口的回调action
public final static String NOTIFY_URL = "";
//微信支付成功支付后跳转的地址
public final static String SUCCESS_URL = "";
/**
* 微信支付接口地址
*/
//微信支付统一接口(POST)
public final static String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
其中,APPID 、APP_SECRECT ,在登录微信公众平台(https://mp.weixin.qq.com)-开发者中心页面
MCH_ID 在微信公众平台-微信支付页面
API_KEY 需要登录微信商户平台(https://pay.weixin.qq.com)-账户设置-api安全-设置密钥。(注意密钥设置成功后,设置密钥按钮不变,而且没有查询密钥的地方,这一点很不人性化。一般情况下密钥设置成功10分钟后自动生效)
正式进入开发阶段:
1. OAuth2.0授权,获取code参数(需要在微信公众平台-开发者中心-网页授权获取用户基本信息处添加“授权回调页面域名”)
JSAPI 支付前需要调用 登录授权接口获取到用户的 Openid 。所以需要做一次授权,这次授权是不弹出确认框的。其实质就是在用户访问你的网站微信支付页面(例如:http://www.abc.cn/wxpay/jspay)时,跳转到
https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx1111111111&redirect_uri=http://www.abc.cn/wxpay/jspay&response_type=code&scope=snsapi_base&state=123#wechat_redirect
以上链接重定向到http://www.abc.cn/wxpay/jspay,返回code和state参数,以此来获得code参数,并根据code来获得授权access_token及openid
2. OAuth2.0授权,获取openid参数
String URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+ConfigUtil.APPID+"&secret="+ConfigUtil.APP_SECRECT+"&code="+code+"&grant_type=authorization_code";
Map<String, Object> dataMap = new HashMap<String, Object>();
String temp = HttpConnect.getInstance().doGetStr(URL);//返回json格式字符串
JSONObject jsonObj = JSONObject.fromObject(temp);
if(jsonObj.containsKey("errcode")){
System.out.println(temp);
}
openId = jsonObj.getString("openid");
这一步的最终结果就是获得了当前用户的openid
3. 调用统一支付接口
统一支付是JSAPI/NATIVE/APP各种支付场景下生成支付订单,返回预支付订单号的接口,目前微信支付所有场景均使用这一接口。
SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
parameters.put("appid", ConfigUtil.APPID);
parameters.put("mch_id", ConfigUtil.MCH_ID);
parameters.put("nonce_str", PayCommonUtil.CreateNoncestr());
parameters.put("body", "订单:"+goodsOrder.orderNo);
parameters.put("out_trade_no", goodsOrder.orderNo);
parameters.put("total_fee", total_fee);
parameters.put("spbill_create_ip",IPUtils.getIpAddr(request));
parameters.put("notify_url", ConfigUtil.NOTIFY_URL);
parameters.put("trade_type", "JSAPI");
parameters.put("openid", openId);//oauth2认证后得到的openid
String sign = PayCommonUtil.createSign("UTF-8", parameters);//必须在商户平台设置api密钥,否则签名无效
parameters.put("sign", sign);
String requestXML = PayCommonUtil.getRequestXml(parameters);//post请求,必须将参数组织生成xml格式文档发送
String result =CommonUtil.httpsRequest(ConfigUtil.UNIFIED_ORDER_URL, "POST", requestXML);//调用微信统一接口获取prepayId
Map<String, String> map = XMLUtil.doXMLParse(result);//解析微信返回的信息(仍然返回xml格式),以Map形式存储便于取值
System.out.println("prepay_id="+map.get("prepay_id"));
4. 调用微信支付JS API接口
如果prepay_id != null,则说明订单已生成,可以发起支付请求。
//设置微信支付接口需要的参数
SortedMap<Object,Object> obj = new TreeMap<Object,Object>();
obj.put("appId", ConfigUtil.APPID);
obj.put("timeStamp", Long.toString(new Date().getTime()));
obj.put("nonceStr", PayCommonUtil.CreateNoncestr());
obj.put("package", "prepay_id="+map.get("prepay_id"));
obj.put("signType", ConfigUtil.SIGN_TYPE);
String paySign = PayCommonUtil.createSign("UTF-8", obj);
obj.put("paySign", paySign); //paySign的生成规则和Sign的生成规则一致
obj.put("sendUrl", ConfigUtil.SUCCESS_URL); //付款成功后跳转的页面
if(request.headers.get("user-agent")!=null){
String userAgent = request.getHeader("user-agent");
char agent = userAgent.charAt(userAgent.indexOf("MicroMessenger")+15);
obj.put("agent", new String(new char[]{agent}));//微信版本号,用于判断用户手机微信的版本是否是5.0以上版本。
}
将这些参数传到前端页面,赋值给JS API函数,发起支付请求。
前端页面示例如下:
<div class="pay_but"><a href="javascript:void(0);" onclick="onBridgeReady();">确认支付</a></div>
<script type="text/javascript">
function onBridgeReady(){
if(parseInt(${obj.agent})<5){
alert("您的微信版本低于5.0无法使用微信支付");
return;
}
WeixinJSBridge.invoke('getBrandWCPayRequest',{
"appId" : "${obj.appId}",
"timeStamp": "${obj.timeStamp}",
"nonceStr" : "${obj.nonceStr}",
"package" : "${obj.packageValue}",
"signType" : "${obj.signType}",
"paySign" : "${obj.paySign}"
},function(res){
//alert(res.err_msg);
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
window.location.href="${obj.sendUrl}";
}else{
window.location.href="/mobile/front/payment?id=${goodsOrder.payId}";
}
});
}
</script>
点击“确认支付”按钮,发起支付请求,至此开发结束!
相关工具方法:
//sign签名
public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){
StringBuffer sb = new StringBuffer();
Set es = parameters.entrySet();
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=" + ConfigUtil.API_KEY);
//System.out.println("sign xml ====== "+sb.toString());
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
//随机字符串
public static String CreateNoncestr(int length) {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String res = "";
for (int i = 0; i < length; i++) {
Random rd = new Random();
res += chars.indexOf(rd.nextInt(chars.length() - 1));
}
return res;
}
public static String CreateNoncestr() {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
String res = "";
for (int i = 0; i < 16; i++) {
Random rd = new Random();
res += chars.charAt(rd.nextInt(chars.length() - 1));
}
return res;
}
//将请求参数转换为xml格式的string
public static String getRequestXml(SortedMap<Object,Object> parameters){
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = parameters.entrySet();
Iterator it = es.iterator();
while(it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
String k = (String)entry.getKey();
String v = (String)entry.getValue();
if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {
sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
}else {
sb.append("<"+k+">"+v+"</"+k+">");
}
}
sb.append("</xml>");
return sb.toString();
}
//发送https请求,返回微信服务器响应的信息
public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.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;
conn.disconnect();
return buffer.toString();
} catch (ConnectException ce) {
log.error("连接超时:{}", ce);
} catch (Exception e) {
log.error("https请求异常:{}", e);
}
return null;
}
//解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if(null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if(children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = XMLUtil.getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
/**
* 获取子结点的xml
* @param children
* @return String
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if(!children.isEmpty()) {
Iterator it = children.iterator();
while(it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if(!list.isEmpty()) {
sb.append(XMLUtil.getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
一些常见报错解决方法:
1. redirect_uri参数错误
可能1:授权目录
支付授权目录是网站发起请求的页面所在目录,并且必须是能通过url地址访问的(与真实物理目录路径无关)。注意这个目录在注册填写时,需要精确到最细一级的且使用名称后直接加文件名,不可再增加 or 删减目录。
可能2:网页授权
当开发者使用微支付的“JS API”支付时,这种支付需要网页授权,先获取code,再拿code去获取openid和prepay_id。这个网页授权需要登录微信公众平台,点击左 侧菜单“开发者中心”,在右侧“接口权限列表”中找到“网页账号”,点击最右侧的修改,把测试的域名写进去,注意不要加http。
可能3:网页获取用户基本信息(位置:微信公众号-开发者中心)
这个可能性最大,网页获取用户基本信息中的域名没有填写或填写错误,我遇到的了是这个问题,要确保相应的参数设置没有错误情况下,就检查这一项,很多时候就可以解决了。
可能4:链接地址不存在
在微信端点击相应的按钮,如果是出现链接地址不存在,或者配置错误也会出现这个问题,因此在配置内部链接网址的、目录的时候,一定要准确,不然就会出现以上图问题,这个比较好解决,检查,重新设置链接,这个有时要求有一定的代码基础。
2. access_denied
支付授权目录,注意看文档,大小写关系很大,url地址上最好都是小写字母,否则容易报此错误。
3. access_not_allow
将测试人的微信帐号加入白名单。
4. 签名无效
登录微信商户平台(https://pay.weixin.qq.com)-账户设置-api安全-设置密钥。注意密钥设置成功后,设置密钥按钮不变,而且没有查询密钥的地方,这一点很不人性化。一般情况下密钥设置成功10分钟后自动生效。
分享到:
相关推荐
微信公众号开发 从前台到Java后台 调用微信JS接口分享朋友圈,博客中的源代码。数据库请到博客中的GitHub链接自取,建好数据库后需要修改数据库的配置信息。
本篇文章主要介绍了java实现微信H5支付方法详解,非常具有实用价值,需要的朋友可以参考下
首先微信公众号里面得H5开发语音录入,试过各种方法,由于是java渣渣,页面技术只是了解简单得,因此在使用标签得时候,本身并不能适用于当前得需求,因此最后只能选择了微信自带得录音功能,使用微信提供的接口,...
本代码为本人最近开发用过的demo,前端后端的代码都已包括,只需根据自己的需求稍作修改即可;
总的来说,调用微信和支付宝支付接口涉及到的技术点包括API申请、请求签名、支付请求生成、支付结果处理及回调验证等多个环节,都需要开发者具备扎实的Java编程基础和对支付协议的深入理解。通过以上介绍,希望能为...
CRMEB 开源商城系统Java版,基于Java+Vue+Uni-app开发,在微信公众号、小程序、H5移动端都能使用,代码全开源无加密,独立部署,二开很方便,还支持免费商用,能满足企业新零售、分销推广、拼团、砍价、秒杀等多种...
综上所述,这个开源项目为想要搭建微信小程序商城的开发者提供了一个全面的起点,涵盖了从服务器端到客户端的全部开发工作,通过Java和uniapp的结合,降低了开发难度,提高了开发效率,是电商类小程序开发的一个理想...
微信硬件蓝牙开发jsapi+SpringMVC+H5+jQuery是一项技术集成,旨在利用微信的硬件接口能力,结合Web开发框架,实现与蓝牙设备的交互。这个项目涉及到的知识点广泛,主要包括以下几个方面: 1. **微信硬件jsapi**:...
微信h5授权获取用户openId的方法和步骤,用于用户登录和注册,代码已经封装好,下载就能用
内置微信及支付宝支付(PC/H5/APP/小程序/公众号)接口调用获取数据处理,微信公众号消息模板接口,及对应回调数据解析处理,免对接开发。只需配置相应应用的APPID、KEY、证书、真实回调地址等支付相关信息即可使用...
总的来说,Java微信企业号开发中发送消息的过程包括:建立连接、处理回调请求、解密消息、获取接口访问凭证、调用发送接口并构造相应的消息数据。理解这些步骤并正确实现,就能使应用具备与微信企业号用户进行多样化...
Java商城 免费 开源 CRMEB商城JAVA版,SpringBoot + Maven + Swagger + Mybatis Plus + Redis + Uniapp +Vue+elementUI 包含移动端、小程序、PC后台、Api接口;有产品、用户、购物车、订单、积分、优惠券、营销、...
CRMEB Java版商城系统是一款基于Java + Uni-app 开发的新零售社交电商系统,能够真正帮助企业基于微信公众号、小程序、移动端等,实现会员管理、数据分析,精准营销的电子商务管理系统。 系统代码全开源无加密,独立...
uni-app(底层框架Vue),一套代码直接生成h5(直接部署公众号),微信小程序,可以打包混合app,一箭三雕,经济又好用,也是市场主流的开发框架之一 后台管理】:vue-element-admin,是目前国内外都很火的一套后台管理...
支付接口的集成是家政预约小程序的关键环节,一般会接入微信支付,用户在选择服务并提交订单后,可以通过小程序内的支付入口进行在线支付,这需要与微信支付SDK进行对接,处理支付状态的回调,确保支付安全。...
CRMEB 开源商城系统Java版,基于Java+Vue+Uni-app开发,在微信公众号、小程序、H5移动端都能使用,代码全开源无加密,独立部署,二开很方便,还支持免费商用,能满足企业新零售、分销推广、拼团、砍价、秒杀等多种...
CRMEB V4.4标准版打通版商城源码小程序公众号H5 App商城源码安装教程运行环境:php/mysql介绍:服务器环境推荐要求:Nignx/Apache/IISPHP 7.1 ~ 7.4MySQL 5.7Redis技术亮点1.自主研发独立客服系统;2.管理端页面使用...
微信作为中国最流行的社交平台,其开放平台提供了丰富的API接口,使得开发者能够将H5应用无缝嵌入微信环境。通过微信登录授权、分享、支付等功能,可以提升用户体验,同时利用微信的社交网络进行推广。 4. **安卓...
微信网页登录授权是微信开放平台提供的一项功能,允许开发者通过特定的API接口,使用户能够在自己的网站上使用微信账号进行登录。本项目是基于Java语言实现的微信网页登录授权的Demo,非常适合对微信开发感兴趣或者...
基于Java+Vue+Uni开发,微信公众号、小程序、H5移动端都能使用,代码全开源无加密,独立部署,二开很方便,还支持免费商用,能满足企业新零售、分销推广、拼团、砍价、秒杀等多种经营需求,自用、做二开项目都很合适...