1. Подготовка
Прежде всего, давайте жаловаться на оплату WeChat. Есть несколько моделей платежей, которые он поддерживает, но официальные документы особенно разбросаны, и даже нет нескольких приличных демо, связанных с Java. Я никогда раньше не делал платеж. Я был очень ошеломлен этим в начале. Я наконец -то прошел через два дня. Я написал это здесь, чтобы насладиться будущими поколениями!
Что касается подготовки к работе, официальный адрес документа «WeChat Scan Code Model 2» находится здесь https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1, вы можете сначала посмотреть. На самом деле, есть следующие вещи, которые нужно подготовить:
Среди них APP_ID и APP_SECRET можно найти на общедоступной платформе, в то время как MCH_ID и API_KEY находятся на торговой платформе. В частности, API_KEY должен быть установлен на торговой платформе. Для «режима платежного кода WeChat Code 2» (платеж и обратный вызов) будут использоваться только APP_ID, MCH_ID и API_KEY, и больше ничего не используется.
Я не буду говорить о среде развития. Независимо от того, являетесь ли вы Springmvc, Struts2 или Direct Serverlet, это почти то же самое. Пока вы можете убедиться, что соответствующий метод может быть вызван. Что касается цитирования сторонних баночных пакетов, я использовал только JDom, который управляет XML здесь. Помните, что это версия 1.*, а не последняя 2.* на официальном веб -сайте, и они несовместимы. В частности, это jdom-1.1.3.jar, пакет зависимости Jaxen-1.1.6.jar. Для этих двух пакетов я не использовал HTTPClient, используемый в некоторых примерах. Это кажется ненужным, и пакет зависимости очень сложный. Конечно, ты Мейвен, когда я этого не сказал.
2. Развитие и практическая боевая бой
1. Во -первых, подключитесь к интерфейсу WeChat и получите QR -код WeChat платеж.
public String weixin_pay () Throws Exception {// Информация об учетной записи string appid = payconfigutil.app_id; // appid // string appsecret = payconfigutil.app_secret; // appsecret string mch_id = payconfigutil.mch_id; // Номер бизнеса String Key = payConfigutil.api_key; // ключ строка currtime = paycommonutil.getCurrtime (); String strime = curtrime.substring (8, curtrime.length ()); String strrandom = paycommonutil.buildrandom (4) + ""; String nonce_str = strime + strrandom; Строка order_price = 1; // цена Примечание: единица цены разделена на строковое тело = "Goaldsssssss"; // имя продукта string out_trade_no = "11338"; // Номер заказа // Получить инициализацию компьютера IP String SPBILL_CREATE_IP = PAYCONFIGUTIL.CREATE_IP; // string interface interface notify_url = payconfigutil.notify_url; String Trade_type = "Native"; SortedMap <Object, Object> PackageParams = new TreeMap <Object, Object> (); packageparams.put ("appid", appid); packageparams.put ("MCH_ID", MCH_ID); packageparams.put ("nonce_str", nonce_str); packageparams.put («тело», тело); packageparams.put ("out_trade_no", out_trade_no); packageparams.put ("total_fee", order_price); packageparams.put ("spbill_create_ip", spbill_create_ip); packageparams.put ("notify_url", notify_url); packageparams.put ("trade_type", trade_type); String sign = paycommonutil.createSign ("utf-8", packageparams, key); packageparams.put ("sign", sign); String requestxml = paycommonutil.getRequestxml (packageParams); System.out.println (requestxml); String resxml = httputil.postdata (payconfigutil.ufdoder_url, requestxml); Карта карта = xmlutil.doxmlparse (resxml); // string return_code = (string) map.get ("return_code"); // string prepay_id = (string) map.get ("prepay_id"); String urlCode = (string) map.get ("code_url"); вернуть UrlCode; }Если ничего неожиданного не происходит, мы получаем URL -адрес платежа с сервера WeChat, который выглядит как weixin: // wxpay/bizpayurl? Pr = pixxxxx. После этого нам нужно сгенерировать QR -код для этого URL -адреса, а затем мы можем использовать наш мобильный телефон WeChat для сканирования кода для оплаты. Есть много способов генерировать QR -коды. Пожалуйста, возьмите свои собственные потребности. Я предоставляю здесь интерфейс Google QR -кода здесь:
Public Static String QrFromGoogle (String CHL) бросает исключение {int widhthealt = 300; String ec_level = "l"; int margin = 0; chl = urlencode (CHL); String qrfromgoogle = "http://chart.apis.google.com/chart?chs=" + widhtheight + "x" + widhtheight + "& cht = qr & chld =" + ec_level + "|" + margin + "& chl =" + chl; return qrfromgoogle; } // Специальная обработка символов общедоступная статическая строка urlencode (String src) бросает UnsupportedEncodingException {return urlencoder.encode (src, "UTF-8"). Заменить ("+", "%20"); } Приведенный выше код включает в себя несколько классов инструментов: Payconfigutil, Paycommonutil, Httputil и Xmlutil. Среди них Payconfigutil помещает некоторые конфигурации и пути, упомянутые выше. Paycommonutil включает в себя несколько методов получения текущего события, генерируя случайные строки, получение сигнатур параметров и сплайсинг XML. Код заключается в следующем:
Public Class PayCommonUtil { /*** Будь то правильная подпись, правило: сортируйте по имени параметра AZ, а параметры, которые встречаются с пустыми значениями, не участвуют в подписи. * @return boolean */ public static boolean iStenPaysign (String SearnicEncoding, SortedMap <Object, Object> PackageParams, String api_key) {StringBuffer sb = new StringBuffer (); Set es = packageParams.EntrySet (); Итератор IT = es.iterator (); while (it.hasnext ()) {map.entry entry = (map.entry) it.next (); String k = (string) entry.getKey (); String v = (string) entry.getValue (); if (! "sign" .equals (k) && null! = v &&! "". equals (v)) {sb.append (k + "=" + v + "&"); }} sb.append ("key =" + api_key); // файл Сводная строка mysign = md5util.md5encode (sb.tostring (), haremencoding) .tolowercase (); String tenPaysign = ((string) packageParams.get ("sign")). ToLowerCase (); //System.out.println(tenpaysign + "" + mysign); вернуть TenPaysign.equals (mySign); } / ** * @Author * @Date 2016-4-22 * @description: подпись подписи * @param harementencoding * format format * @param Параметры * Параметры запроса * @return * / public Static String CreateSign (String Bearnencoding, SortedMap <Object> PackageParams, String api_key) {stringBuffer SB = StringBuffer (); Set es = packageParams.EntrySet (); Итератор IT = es.iterator (); while (it.hasnext ()) {map.entry entry = (map.entry) it.next (); String k = (string) entry.getKey (); String v = (string) entry.getValue (); if (null! = v &&! "". Equals (v) &&! "Знак" .equals (k) &&! ". }} sb.append ("key =" + api_key); String sign = md5util.md5encode (sb.tostring (), incerviceencoding) .touppercase (); вернуть знак; } / ** * @Author * @Date 2016-4-22 * @Description: конвертировать параметры запроса в xml format string * @param Параметры * Параметры запроса * @return * / public static String getRequestxml (sortedMap <объект, объект> параметры) {StringBuffer SB = new StringBuffer (); SB.Append ("<xml>"); Установить es = parameters.EntrySet (); Итератор IT = es.iterator (); while (it.hasnext ()) {map.entry entry = (map.entry) it.next (); String k = (string) entry.getKey (); String v = (string) entry.getValue (); if («Прикрепить». EqualsIgnoreCase (k) || «тело». EqualsIgnoreCase (k) || «Знак" .EqualsIgnoreCase (k)) {sb.append ("<" + k + ">" + "<! [cdata [" + v + "]]> </" + k + ">"); } else {sb.append ("<" + k + ">" + v + "</" + k + ">"); }}} sb.append ("</xml>"); вернуть sb.toString (); } /*** Уберите случайное положительное целое число от указанного размера длины. * * @param длина * int Установите длину полученного случайного числа. Длина менее 11 * @return int возвращает сгенерированное случайное число. */ public static int buildrandom (int length) {int num = 1; Double Random = math.random (); if (случайный <0,1) {случайный = случайный + 0,1; } for (int i = 0; i <length; i ++) {num = num * 10; } return (int) ((случайный * num)); } / ** * Получить текущее время yyyymmddhhmmss * * @return String * / public Static String getCurrtime () {date now = new Date (); SimpleDateFormat Outformat = new SimpleDateFormat ("yyyyMmddhhmmss"); String s = OutFormat.format (сейчас); возврат S; }}Httputil и Xmlutil следующие:
открытый класс httputil {private static final log logger = logs.get (); Частный окончательный статический int connect_timeout = 5000; // в миллионах секунд частная окончательная статическая строка default_encoding = "utf-8"; public Static String postdata (String urlstr, String Data) {return postdata (urlstr, data, null); } public Static String postData (String urlStr, String Data, String ContentType) {BufferedReader Reader = null; try {url url = new url (urlstr); Urlconnection conn = url.openconnection (); conn.setDoOutput (true); conn.setConnecttimeout (connect_timeout); conn.setReadtimeOut (connect_timeout); if (contentType! = null) conn.setRequestProperty ("content-type", contentType); Outputstreamwriter writer = new outputstreamwriter (conn.getOutputStream (), default_encoding); if (data == null) data = ""; writer.write (data); writer.flush (); writer.close (); reader = new BufferedReader (new InputStreamReader (conn.getInputStream (), default_encoding)); StringBuilder SB = new StringBuilder (); String line = null; while ((line = reader.readline ())! = null) {sb.append (line); sb.append ("/r/n"); } вернуть sb.toString (); } catch (ioException e) {logger.error («Ошибка подключения к" + urlstr + ":" + e.getMessage ()); } наконец {try {if (reader! = null) reader.close (); } catch (ioException e) {}} return null; }} открытый класс xmlutil { /*** parse xml и вернуть пару клавишных значений элемента первого уровня. Если в элементе первого уровня есть дети, значение этого узла-это XML-данные детского узла. * @param strxml * @return * @Throws JDomeXception * @Throws ioException */public static map doxmlparse (string strxml) бросает jdomexception, ioexception {strxml = strxml.replacefirst ("incoding =/". */",", "incoding =/" 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 (); Документ doc = builder.build (in); Элемент root = doc.getRootelement (); Список списка = root.getChildren (); Итератор IT = list.iterator (); while (it.hasnext ()) {element e = (element) it.next (); String k = e.getName (); Строка v = ""; Список детей = e.getChildren (); if (kids.isempty ()) {v = e.getTextnormalize (); } else {v = xmlutil.getChildRentext (дети); } m.put (k, v); } // Закройте поток в.close (); возврат М; } / ** * Получить XML детского узла * @param Kids * @return String * / public Static String getChildRentext (список детей) {stringBuffer sb = new StringBuffer (); if (! childay.isempty ()) {iterator it = kids.iterator (); while (it.hasnext ()) {element e = (element) it.next (); String name = e.getName (); String value = e.getTextNormale (); Список списка = e.getChildren (); sb.append ("<" + name + ">"); if (! list.isempty ()) {sb.append (xmlutil.getChildRentext (list)); } sb.append (значение); sb.append ("</" + name + ">"); }} return sb.toString (); }} Конечно, есть также класс вычислительных инструментов MD5
открытый класс md5util {private Static String BytearrayToHexString (byte b []) {stringBuffer resultsb = new StringBuffer (); для (int i = 0; i <b.length; i ++) resultsb.append (bytetohexstring (b [i])); return resultsb.toString (); } частная статическая строка BytetoHexString (byte b) {int n = b; if (n <0) n += 256; int d1 = n / 16; int d2 = n % 16; вернуть Hexdigits [D1] + hexdigits [D2]; } public Static String md5encode (String Origin, String charsetName) {String ResultString = null; try {resultString = new String (Origin); MessageDigest MD = MOSSAGEDIGEST.GETINSTANCE ("MD5"); if (charsetName == null || "" .equals (charsetName)) resultString = bytearraytohexstring (md.digest (refferencestring .getbytes ())); иначе ResultString = BytearrayToHexString (md.digest (refulsTring .getBytes (charsetName))); } catch (исключение исключения) {} return resultString; } частная статическая конечная строка hexdigits [] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"}; } 2. Платежный обратный вызов
После завершения платежа WeChat отправит соответствующие результаты оплаты и информацию пользователя по адресу обратного вызова, который мы указали выше. Нам нужно получить обработку и вернуть ответ. При взаимодействии с фоновыми уведомлениями, если WeChat получает ответ торговца без успеха или тайм-аута, и WeChat считает, что уведомление потерпело неудачу, WeChat регулярно повторно инициирует уведомления через определенные стратегии для увеличения уровня успеха уведомлений максимально возможным. (Частота уведомлений 15/15/30/1800/1800/1800/1800/1800/1800/3600, Блок: Секунды)
Что касается интерфейса обратного вызова платежей, мы должны сначала подписать и проверить содержание уведомления о результатах оплаты, а затем провести соответствующий процесс обработки на основе результата оплаты.
public void weixin_notify (httpservlectrequest, httpservletresponse response) вызывает исключение {// Прочитать параметры inputstream inputstream; StringBuffer sb = new StringBuffer (); inputStream = request.getInputStream (); Строка S; BufferedReader in = new BufferedReader (New InputStreamReader (inputStream, "UTF-8")); while ((s = in.readline ())! = null) {sb.append (s); } in.close (); inputstream.close (); // parse xml в карту карты <строка, строка> m = new hashmap <string, string> (); m = xmlutil.doxmlparse (sb.tostring ()); // Фильтр пустые настройки treeMap cordEdmap <object, object> packageparams = new TreeMap <Object, Object> (); Итератор IT = m.KeySet (). Iterator (); while (it.hasnext ()) {string parameter = (string) it.next (); String parametervalue = m.get (параметр); Строка v = ""; if (null! = parametervalue) {v = parametervalue.trim (); } packageparams.put (параметр, v); } // Информация об учетной записи string key = payconfigutil.api_key; // key logger.info (packageparams); // Определите, правильная ли подпись if (paycommonutil.istenpaysign ("utf-8", packageparams, key)) { // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ starts//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- (String) packageparams.get ("out_trade_no"); String total_fee = (string) packageparams.get ("total_fee"); logger.info ("mch_id:"+mch_id); logger.info ("openid:"+openid); logger.info ("is_subscribe:"+is_subscribe); logger.info ("out_trade_no:"+out_trade_no); logger.info ("total_fee:"+total_fee); //////////// 执行自己的业务逻辑 //////////////////////////GURGER.INFO ("Оплата успешно"); // уведомление WeChat. Асинхронное подтверждение успешно. Должен написать это. В противном случае фон будет уведомлен все время. Через восемь раз будет рассмотрено, что транзакция не удалась. resxml = "<xml>" + "<return_code> <! [cdata [успех]]> </return_code>" + "<return_msg> <! [cdata [ok]]> </return_msg>" + "</xml>"; } else {logger.info ("Ошибка оплаты, сообщение об ошибке:" + packageparams.get ("err_code")); resxml = "<xml>" + "<return_code> <! [cdata [fail]]> </return_code>" + "<return_msg> <! [cdata [сообщение пусто]]> </return_msg>" + "</xml>"; } //---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- logger.info ("Проверка подписи уведомления не удалась"); }} Алгоритм проверки подписи аналогичен алгоритму генерации подписи и представлен в классе инструмента PayCommonUtil выше.
3. более поздние истории
Я чувствую, что опыт оплаты сканирования WeChat довольно хорош. Единственным недостатком является то, что соответствующие документы разбросаны. Официальная демонстрация не написана на Java. Я надеюсь, что официальный чиновник WeChat может постепенно улучшить его в будущем!
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.