700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > JAVA微信支付(公众号支付JSAPI-下单)

JAVA微信支付(公众号支付JSAPI-下单)

时间:2020-01-01 14:55:12

相关推荐

JAVA微信支付(公众号支付JSAPI-下单)

微信jsapi支付下单对于java开发来说只需要调用官方文档的“统一下单”接口,然后将返回的xml字符串转成Map返回给前台,而前端需要获取返回的参数唤起支付


一、JSAPI下单所需参数

appid:公众号appidsecret: 公众号secretmch_id:商户号paternerKey:商户秘钥

二、支付授权目录以及授权域名

参考官方文档:https://pay./wiki/doc/api/jsapi.php?chapter=7_3

三、开发流程

1.获取openid

参考官方文档:https://developers./doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

2.下载官方sdk

下载地址:https://pay./wiki/doc/api/jsapi.php?chapter=7_7&index=6

主要是用其中的WXPayUtil工具类中的一些方法

3.支付下单接口开发

统一下单官方文档:https://pay./wiki/doc/api/jsapi.php?chapter=9_1

3.1 必填参数取值

其中的必填参数有:

appid APPID (已有)mch_id 商户ID (已有)nonce_str:随机字符串用WXPayUtil中的generateNonceStr()即可,就是生成UUID的方法sign 签名:用WXPayUtil中的generateSignature

/*** 生成签名** @param data 待签名数据* @param key API密钥* @return 签名*/public static String generateSignature(final Map<String, String> data, String key) throws Exception {return generateSignature(data, key, SignType.MD5);}

body 所支付的名称out_trade_no 咱们自己所提供的订单号,需要唯一total_fee支付金额 单位:分这个地方一定要注意

/*** 元转换成分** @param amount* @return*/public static String getMoney(String amount) {if (amount == null) {return "";}// 金额转化为分为单位String currency = amount.replaceAll("\\$|\\¥|\\,", ""); //处理包含, ¥ 或者$的金额int index = currency.indexOf(".");int length = currency.length();Long amLong = 0l;if (index == -1) {amLong = Long.valueOf(currency + "00");} else if (length - index >= 3) {amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));} else if (length - index == 2) {amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);} else {amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");}return amLong.toString();}

spbill_create_ip IP地址

//获取请求ip地址String ip = request.getHeader("x-forwarded-for");if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ip = request.getHeader("Proxy-Client-IP");}if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ip = request.getHeader("WL-Proxy-Client-IP");}if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)){ip = request.getRemoteAddr();}if(ip.indexOf(",")!=-1){String[] ips = ip.split(",");ip = ips[0].trim();}

notify_url 回调地址,一定要公网可以访问,通过回调接口根据参数以及自己系统的业务逻辑修改订单状态trade_type 支付类型 JSAPIopenid 支付人的微信公众号对应的唯一标识

3.2 请求微信统一下单接口

此处用到post请求的方法,请求微信"统一下单接口https://api.mch./pay/unifiedorder

接口返回结果:

<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[wx11101639507cbf6ffd8b0779950874]]></prepay_id><trade_type><![CDATA[JSAPI]]></trade_type></xml>

请求下单接口最重要的就是获取参数prepay_id,然后将前端所需参数以map形式返回,这个地方有一个需要注意一下,前端需要一个字段名为package的参数,因为package是java的关键字,所以最好以Map返回

3.3 JSAPI调起支付

前端所需参数描述:

前端示例

function onBridgeReady(){WeixinJSBridge.invoke('getBrandWCPayRequest', {"appId":"wx2421b1c4370ec43b",//公众号ID,由商户传入"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();}

3.具体代码

Map<String, String> paraMap = new HashMap<String, String>();paraMap.put("appid", merchantTenpayEntity.getWeAppid()); paraMap.put("body", order.getGoodsName()); paraMap.put("mch_id", merchantTenpayEntity.getWeMchId()); paraMap.put("nonce_str", WXPayUtil.generateNonceStr()); paraMap.put("openid", openId);paraMap.put("out_trade_no", order.getNo());//订单号paraMap.put("spbill_create_ip", ip);paraMap.put("total_fee",WChartUtils.getMoney(order.getTotalAmt().toString()));paraMap.put("trade_type", "JSAPI"); paraMap.put("notify_url",notifyUrl);// 此路径是微信服务器调用支付结果通知路径随意写String sign = WXPayUtil.generateSignature(paraMap, merchantTenpayEntity.getKeyPair());paraMap.put("sign", sign);String xml = WXPayUtil.mapToXml(paraMap);//将所有参数(map)转xml格式log.info("微信统一下单请求xml为->{}"+xml);String unifiedorder_url = "https://api.mch./pay/unifiedorder";String xmlStr = HttpRequest.sendPost(unifiedorder_url, xml);//发送post请求"统一下单接口"返回预支付id:prepay_idlog.info("微信统一下单返回xml为->{}"+xmlStr);//以下内容是返回前端页面的json数据String prepay_id = "";//预支付idif (xmlStr.indexOf("SUCCESS") != -1) {Map<String, String> map = WXPayUtil.xmlToMap(xmlStr); prepay_id = (String) map.get("prepay_id"); }Map<String, String> payMap = new HashMap<String, String>();payMap.put("appId", merchantTenpayEntity.getWeAppid()); payMap.put("timeStamp", WXPayUtil.getCurrentTimestamp()+""); payMap.put("nonceStr", WXPayUtil.generateNonceStr()); payMap.put("signType", "MD5"); payMap.put("package", "prepay_id=" + prepay_id); String paySign = WXPayUtil.generateSignature(payMap, merchantTenpayEntity.getKeyPair()); payMap.put("paySign", paySign);return payMap;

merchantTenpayEntity.getKeyPair()为paternerKey(商户秘钥)

4.附WXPayUtil代码

package com.livingspace.mon.wechat;import com.alibaba.fastjson.JSONObject;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.client.config.RequestConfig;import org.apache.http.client.methods.HttpPost;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.ssl.SSLContexts;import org.apache.http.util.EntityUtils;import org.jdom2.Document;import org.jdom2.Element;import org.jdom2.input.SAXBuilder;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.core.io.ClassPathResource;import org.xml.sax.InputSource;import javax.crypto.Mac;import javax.crypto.spec.SecretKeySpec;import .ssl.SSLContext;import javax.servlet.http.HttpServletRequest;import java.io.BufferedInputStream;import java.io.IOException;import java.io.InputStream;import java.io.StringReader;import .InetAddress;import java.security.KeyStore;import java.security.MessageDigest;import java.util.*;/*** 微信工具类**/public class WChartUtils {private static Logger log = LoggerFactory.getLogger(WChartUtils.class);//公众账号IDpublic final static String APPID = "aaa";//商户号public final static String MCHID = "bbb";//商户密钥public final static String APPSECRET = "ccc";//虽然官方文档不是必须参数,但是不送有时候会验签失败public final static String MD5 = "MD5";//退款需要的证书路径public final static String certifi_path = "/data/...";//H5支付标记public final static String trade_type = "JSAPI";//调用统一下单时需要提供得回调接口public final static String callback_notify = "http://.../sub/json/notifyOrder";//获取code接口public final static String getCodeUrl = " https://open./connect/oauth2/authorize?appid=" + APPID + "&redirect_uri=http://114./weixin/login&response_type=code&scope=snsapi_base&state=123#wechat_redirect";//获取网页授权登录access_token 接口地址Getpublic final static String OAUTH_ACCESS_TOKEN_URL = "https://api./sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";//微信统一下单接口public final static String ORDER_URL = "https://api.mch./pay/unifiedorder";//微信退款接口public final static String REFUND_URL = "https://api.mch./secapi/pay/refund";//微信查询订单public final static String QUERY_ORDER_STATUS = "https://api.mch./pay/orderquery";/*** Md5 加密工具** @param s String not null* @return 加密后的 MD5 值 32位大写的*/public static String md5(String s) {char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};try {byte[] btInput = s.getBytes();// 获得MD5摘要算法的 MessageDigest 对象MessageDigest mdInst = MessageDigest.getInstance("MD5");// 使用指定的字节更新摘要mdInst.update(btInput);// 获得密文byte[] md = mdInst.digest();// 把密文转换成十六进制的字符串形式int j = md.length;char str[] = new char[j * 2];int k = 0;for (int i = 0; i < j; i++) {byte byte0 = md[i];str[k++] = hexDigits[byte0 >>> 4 & 0xf];str[k++] = hexDigits[byte0 & 0xf];}return new String(str);} catch (Exception e) {e.printStackTrace();return null;}}/*** 生成 HMACSHA256** @param data 待处理数据* @param key 密钥* @return 加密结果* @throws Exception*/public static String HMACSHA256(String data, String key) throws Exception {Mac sha256_HMAC = Mac.getInstance("HmacSHA256");SecretKeySpec secret_key = new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256");sha256_HMAC.init(secret_key);byte[] array = sha256_HMAC.doFinal(data.getBytes("UTF-8"));StringBuilder sb = new StringBuilder();for (byte item : array) {sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));}return sb.toString().toUpperCase();}/*** 生成签名. 注意,若含有sign_type字段,必须和signType参数保持一致。** @param data 待签名数据* @return 签名*/public static String generateSignature(Map<String, String> data) throws Exception {Set<String> keySet = data.keySet();String[] keyArray = keySet.toArray(new String[keySet.size()]);Arrays.sort(keyArray);StringBuilder sb = new StringBuilder();for (String k : keyArray) {if ("sign".equals(k)) {continue;}if (data.get(k).trim().length() > 0) // 参数值为空,则不参与签名sb.append(k).append("=").append(data.get(k).trim()).append("&");}sb.append("key=").append(APPSECRET);return md5(sb.toString()).toUpperCase();}/*** 生成字母+数字(8位)随机数** @param length 长度* @return val 转换好的文本*/public static String getStringRandom(int length) {String val = "";Random random = new Random();//length为几位密码for (int i = 0; i < length; i++) {String charOrNum = random.nextInt(2) % 2 == 0 ? "char" : "num";//输出字母还是数字if ("char".equalsIgnoreCase(charOrNum)) {//输出是大写字母还是小写字母int temp = random.nextInt(2) % 2 == 0 ? 65 : 97;val += (char) (random.nextInt(26) + temp);} else if ("num".equalsIgnoreCase(charOrNum)) {val += String.valueOf(random.nextInt(10));}}return val;}/*** 元转换成分** @param amount* @return*/public static String getMoney(String amount) {if (amount == null) {return "";}// 金额转化为分为单位String currency = amount.replaceAll("\\$|\\¥|\\,", ""); //处理包含, ¥ 或者$的金额int index = currency.indexOf(".");int length = currency.length();Long amLong = 0l;if (index == -1) {amLong = Long.valueOf(currency + "00");} else if (length - index >= 3) {amLong = Long.valueOf((currency.substring(0, index + 3)).replace(".", ""));} else if (length - index == 2) {amLong = Long.valueOf((currency.substring(0, index + 2)).replace(".", "") + 0);} else {amLong = Long.valueOf((currency.substring(0, index + 1)).replace(".", "") + "00");}return amLong.toString();}/*** description: 解析微信通知xml** @param xml* @return* @author ex_yangxiaoyi* @see*/@SuppressWarnings({"unused", "rawtypes", "unchecked"})public static Map parseXmlToList(String xml) {Map retMap = new HashMap();try {StringReader read = new StringReader(xml);// 创建新的输入源SAX 解析器将使用 InputSource 对象来确定如何读取 XML 输入InputSource source = new InputSource(read);// 创建一个新的SAXBuilderSAXBuilder sb = new SAXBuilder();// 通过输入源构造一个DocumentDocument doc = (Document) sb.build(source);Element root = doc.getRootElement();// 指向根节点List<Element> es = root.getChildren();if (es != null && es.size() != 0) {for (Element element : es) {retMap.put(element.getName(), element.getValue());}}} catch (Exception e) {e.printStackTrace();}return retMap;}/*** 获取用户实际ip** @param request* @return*/public static String getIpAddr(HttpServletRequest request) {String ipAddress = request.getHeader("x-forwarded-for");if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("Proxy-Client-IP");}if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("WL-Proxy-Client-IP");}if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getRemoteAddr();if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {//根据网卡取本机配置的IPInetAddress inet = null;try {inet = InetAddress.getLocalHost();} catch (Exception e) {e.printStackTrace();}ipAddress = inet.getHostAddress();}}//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割if (ipAddress != null && ipAddress.length() > 15) {//"***.***.***.***".length() = 15if (ipAddress.indexOf(",") > 0) {ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));}}return ipAddress;}private static int socketTimeout = 10000;// 连接超时时间,默认10秒private static int connectTimeout = 30000;// 传输超时时间,默认30秒private static RequestConfig requestConfig;// 请求器的配置private static CloseableHttpClient httpClient;// HTTP请求器/*** 通过Https往API post xml数据** @param url API地址* @param xmlObj 要提交的XML数据对象* @return*/public static String postData(String url, String xmlObj) {// 加载证书try {initCert();} catch (Exception e) {e.printStackTrace();}String result = null;HttpPost httpPost = new HttpPost(url);// 得指明使用UTF-8编码,否则到API服务器XML的中文不能被成功识别StringEntity postEntity = new StringEntity(xmlObj, "UTF-8");httpPost.addHeader("Content-Type", "text/xml");httpPost.setEntity(postEntity);// 根据默认超时限制初始化requestConfigrequestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();// 设置请求器的配置httpPost.setConfig(requestConfig);try {HttpResponse response = null;try {response = httpClient.execute(httpPost);} catch (IOException e) {e.printStackTrace();}HttpEntity entity = response.getEntity();try {result = EntityUtils.toString(entity, "UTF-8");} catch (IOException e) {e.printStackTrace();}} finally {httpPost.abort();}return result;}/*** 加载证书** @throws Exception*/private static void initCert() throws Exception {// 证书密码,默认为商户IDString key = MCHID;// 证书的路径// 指定读取证书格式为PKCS12KeyStore keyStore = KeyStore.getInstance("PKCS12");// 读取本机存放的PKCS12证书文件ClassPathResource cp = new ClassPathResource(certifi_path);InputStream instream = cp.getInputStream();try {// 指定PKCS12的密码(商户ID)keyStore.load(instream, key.toCharArray());} finally {instream.close();}SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();SSLConnectionSocketFactory sslsf =new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1"}, null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();}}


总结

第一次写博客,有很多的不足,如果有什么疏漏还得麻烦大家随时联系,请大神勿喷.谢谢大家

无论是H5支付还是JSAPI支付对于回调、退款、对账接口都是可以共用的,所以打算单独写出来。

下面会陆续把微信H5下单以及退款、回调。对账、支付宝下单、支付宝退款、支付宝回调以及支付宝对账都写出来,还请大家期待

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。