微信支付Java开发服务端详解编程语言
想要调用微信支付第一就是获取openid:
获取openid的方法:
首先你需要在前台获取到code,
在确保微信公众账号拥有授权作用域(scope参数)的权限的前提下(服务号获得高级接口后,默认拥有scope参数中的snsapi_base和snsapi_userinfo),引导关注者打开如下页面:
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID redirect_uri=REDIRECT_URI response_type=code scope=SCOPE state=STATE#wechat_redirect
若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限。
scope两种参数的说明:
1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。
在公众号的自定义菜单中设置的url为
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID redirect_uri=URL response_type=code scope=snsapi_userinfo state=STATE connect_redirect=1#wechat_redirect
其中APPID为公众号ID,URL是你要访问的网址,注意网页要进行URLEncoder编码。
如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE state=STATE。若用户禁止授权,则重定向后不会带上code参数,仅会带上state参数redirect_uri?state=STATE
code说明 :
code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
这样在前台就可以直接获取到url当中的code值,
var code = getQueryString("code"); function getQueryString(name) { var reg = new RegExp("(^| )" + name + "=([^ ]*)( |$)", "i"); var r = window.location.search.substr(1).match(reg); if (r != null) return unescape(r[2]); return null; }
获取到了code的值后,就可以去请求openid了。
尤其注意:由于公众号的secret和获取到的access_token安全级别都非常高,必须只保存在服务器,不允许传给客户端。后续刷新access_token、通过access_token获取用户信息等步骤,也必须从服务器发起。
获取code后,请求以下链接获取access_token:
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID secret=SECRET code=CODE grant_type=authorization_code
正确时返回的JSON数据包如下:
{
“access_token”:”ACCESS_TOKEN”,
“expires_in”:7200,
“refresh_token”:”REFRESH_TOKEN”,
“openid”:”OPENID”,
“scope”:”SCOPE”
}
这样就获取到了access_token和openid了。
有了openid,我们就可以去获取到pre_id。
获取pre_id的接口地址为:https://api.mch.weixin.qq.com/pay/unifiedorder
请求参数详见微信支付官方文档
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
其中注意attach参数,这个是附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。这个字段可以传我们的业务数据,用来后续的业务处理。
notify_url参数,这个回调地址一定要写外网能直接访问的地址(可以直接把网址放到浏览器上,看看是不是能直接访问),如果写内网地址是收不到回调信息的哦。
spbill_create_ip参数,这个参数笔者也不清楚应该怎么传,笔者就传的192.168.1.1,如果你们传这个不好使我要找我。
sign参数,详情见微信支付签名生成规则:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_3
其实微信给的demo中就有现成的sign生成方法
首先先把所有参数放入一个map中
public Map String, Object toMap() Map String, Object map = new HashMap String, Object Field[] fields = this.getClass().getDeclaredFields(); for (Field field : fields) Object obj; try obj = field.get(this); if (obj != null) map.put(field.getName(), obj); catch (IllegalArgumentException e) e.printStackTrace(); catch (IllegalAccessException e) e.printStackTrace(); return map; String sign = Signature.getSign(toMap()); public static String getSign(Map String,Object map){ ArrayList String list = new ArrayList String for(Map.Entry String,Object entry:map.entrySet()){ if(entry.getValue()!=""){ list.add(entry.getKey() + "=" + entry.getValue() + " "); int size = list.size(); String [] arrayToSort = list.toArray(new String[size]); Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER); StringBuilder sb = new StringBuilder(); for(int i = 0; i size; i ++) { sb.append(arrayToSort[i]); String result = sb.toString(); result += "key=" + Configure.getKey();//自己保管的私钥 //Util.log("Sign Before MD5:" + result); result = MD5.MD5Encode(result).toUpperCase(); //Util.log("Sign Result:" + result); return result; }
然后把这个sign放入参数中就可以啦。
请求成功后,微信会返回一段xml数据信息,例如:
xml return_code ![CDATA[SUCCESS]] /return_code return_msg ![CDATA[OK]] /return_msg appid ![CDATA[wx2421b1c4370ec43b]] /appid mch_id ![CDATA[10000100]] /mch_id nonce_str ![CDATA[IITRi8Iabbblz1Jc]] /nonce_str openid ![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]] /openid sign ![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]] /sign result_code ![CDATA[SUCCESS]] /result_code prepay_id ![CDATA[wx201411101639507cbf6ffd8b0779950874]] /prepay_id trade_type ![CDATA[JSAPI]] /trade_type /xml
这里提供一个把XML转换成Map的方法:
public static Map String,Object getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException { //这里用Dom的方式解析回包的最主要目的是防止API新增回包字段 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); InputStream is = Util.getStringStream(xmlString); Document document = builder.parse(is); //获取到document里面的全部结点 NodeList allNodes = document.getFirstChild().getChildNodes(); Node node; Map String, Object map = new HashMap String, Object (); int i=0; while (i allNodes.getLength()) { node = allNodes.item(i); if(node instanceof Element){ map.put(node.getNodeName(),node.getTextContent()); i++; return map; }
到这里我们拿到了prepay_id,这里顺便说一下,如果是扫码支付前面的trade_type参数为NATIVE,再返回的时候会有一个code_url,在前台直接用这个url生成二维码图片就可以了。
下面继续说公众号支付。
前台H5调用微信支付的参数说明见:
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7 index=6
这里什么时间戳,随机字符串最后在服务端生成再传到前台来。
String timestamp = Long.toString(System.currentTimeMillis() / 1000); // 必填,生成签名的时间戳 String nonceStr = UUID.randomUUID().toString(); // 必填,生成签名的随机串
注意package参数为prepay_id=PRERAY_ID一定要带上prepay_id=。
同时生成签名的时候也注意要带着prepay_id=,这块算个坑。
具体前台调起微信支付的代码直接复制微信文档
function onBridgeReady(){ 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,但并不保证它绝对可靠。 if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener(WeixinJSBridgeReady, onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent(WeixinJSBridgeReady, onBridgeReady); document.attachEvent(onWeixinJSBridgeReady, onBridgeReady); }else{ onBridgeReady(); }
这里注意支付成功后那里的判断res.err_msg == “get_brand_wcpay_request:ok”,坑爹的是那里的:是中文的分号,我说回调咋一直都不进呢!!
还有补充,要注意在商户后台配置支付授权目录,否则支付页面是出不来的,我当时没配是一闪而过。
至此能够成功调起微信支付,支付成功后会向notify_url发送支付信息。例如:
xml appid ![CDATA[wx2421b1c4370ec43b]] /appid attach ![CDATA[支付测试]] /attach bank_type ![CDATA[CFT]] /bank_type fee_type ![CDATA[CNY]] /fee_type is_subscribe ![CDATA[Y]] /is_subscribe mch_id ![CDATA[10000100]] /mch_id nonce_str ![CDATA[5d2b6c2a8db53831f7eda20af46e531c]] /nonce_str openid ![CDATA[oUpF8uMEb4qRXf22hE3X68TekukE]] /openid out_trade_no ![CDATA[1409811653]] /out_trade_no result_code ![CDATA[SUCCESS]] /result_code return_code ![CDATA[SUCCESS]] /return_code sign ![CDATA[B552ED6B279343CB493C5DD0D78AB241]] /sign sub_mch_id ![CDATA[10000100]] /sub_mch_id time_end ![CDATA[20140903131540]] /time_end total_fee 1 /total_fee trade_type ![CDATA[JSAPI]] /trade_type transaction_id ![CDATA[1004400740201409030005092168]] /transaction_id /xml
具体参数说明见:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
其中attach参数就是传输的业务数据,这样就可以进行业务处理了。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/13978.html
cjavaphpxml相关文章
- java locale 中国_Java描述语言、国家和地理的类——Locale
- java启动器_JAVA基础:Java 启动器如何查找类
- java环境_Java 开发环境配置
- Java中&、|、&&、||详解
- java高级工程师_一名Java高级工程师需要学什么?
- java递归和迭代_Java中的迭代与递归
- 【Java】jar启动的java程序报错FileNotFoundException
- java事务_Java 事务详解[通俗易懂]
- rtsp 获取视频流 java_Java获取rtsp视频流,实现rtsp流预览功能,并将视频流每帧保存成图片…
- java 事务嵌套_Java事务以及嵌套事务[通俗易懂]
- 【Java 虚拟机原理】垃圾回收算法 ( Java 虚拟机内存分区 | 垃圾回收机制 | 引用计数器算法 | 引用计数循环引用弊端 )
- Java版 微信红包算法详解编程语言
- Java学习笔记之五java数组详解编程语言
- 详解JAVA后端实现统一扫码支付:微信篇编程语言
- java 标准输出与标准错误 out与 err 区别 用法 联系 java中的out与err区别 System.out和System.err的区别 System.out.println和System.err.println的区别 Java重定向System.out和System.err详解编程语言
- Java List.isEmpty()方法:判断集合对象是否为空
- 解锁Java 与 Oracle 的连接之门(java连接oracle)
- 数据处理在Java中处理Redis过期数据(redisjava过期)
- 国内首本系统讲解Java异步编程的书籍-java异步编程实战
- 使用Java去连接MySQL数据库(java jdbc mysql)
- 编程玩转Java之Oracle编程实战(java中的oracle)