Recentemente, fui exposto a algumas coisas sobre o pagamento do WeChat em meu trabalho. Vi que as demos que dei foram todas as versões PHP e não estava realmente satisfeito com os documentos de pagamento do WeChat. Depois de experimentar muitas armadilhas, fiquei ocioso para fazer um resumo.
1. Preparação
Para desenvolver o WeChat, você deve primeiro solicitar uma conta pública. Depois que o aplicativo for bem -sucedido, você será enviado a você por e -mail. A conta pública contém documentos de desenvolvimento, informações necessárias durante o desenvolvimento e consulta de dados para testes.
2. Ferramentas
1. MD5 Classe de ferramenta de criptografia
pacote com.pay.utils.weixin; importar java.security.messagedigest; public class md5util {public final static string md5 (string s) {char hexdigits [] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'e', 'f'}; tente {byte [] btinput = s.getBytes (); // Obtenha o objeto Messagedigest do algoritmo MD5 Digest Messagedigest mdInst = Messagedigest.getInstance ("md5"); // Atualize o Digest MdInst.Update (BTInput); // obtenha o byte ciphertext [] md = mdinst.digest (); // Converta o CipherText em um formulário de sequência hexadecimal int j = md.length; char str [] = novo 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]; } retornar nova string (str); } catch (Exceção e) {e.printStackTrace (); retornar nulo; }}}2. Classe de ferramentas Commonutil , usada para substituir o XML necessário para o WeChat. A seguinte retorna nova string (xml.toString (). GetBytes (), "ISO8859-1"); Altere o UTF-8 na classe de ferramentas para ISO8859-1, caso contrário, o texto chinês na ordem do WeChat aparecerá iluminado e poderá ser exibido corretamente após a alteração.
pacote com.pay.utils.weixin; importar java.io.unsupportEdEncodingException; importar java.net.urlencoder; importar java.util.*; importar java.util.map.entry; classe pública Commonutil {public static string createNoncest (int length) {strings; "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789"; String res = ""; for (int i = 0; i <comprimento; i ++) {aleatório rd = new aleatom (); res += char.indexOf (rd.nextint (chars.length () - 1)); } retornar res; } public static string createNoCest () {string chars = "abcdefghijklmnopqrstStuvwxyzabcdefghijklmnopqrstuvwxyz0123456789"; String res = ""; for (int i = 0; i <16; i ++) {aleatório rd = new aleatom (); res += chars.charat (rd.nextint (chars.length () - 1)); } retornar res; } public static string formatQueryParamap (hashmap <string, string> parâmetros) lança sdkruntimeException {string buff = ""; tente {list <pap.entry <string, string >> infoids = new ArrayList <pap.entry <string, string >> (parameters.entryset ()); Coleções.sort (InfOids, novo comparador <map.entry <string, string >> () {public int compare (map.entry <string, string> o1, map.entry <string, string> o2) {return (o1.getKey ()). Tostring (). Compareto (o2.getkey ()); for (int i = 0; i <infoids.size (); i ++) {map.entry <string, string> item = infoids.get (i); if (item.getKey ()! = "") {buff + = item.getKey () + "=" + urlencoder.encode (item.getValue (), "utf-8") + "&"; }} if (buff.isEmpty () == false) {buff = buff.substring (0, buff.length () - 1); }} Catch (Exceção e) {lança nova sdkruntimeException (e.getMessage ()); } retornar buff; } public static string formatBizQueryParamap (hashmap <string, string> paramap, urlencode boolean) lança sdkruntimeException {string buff = ""; tente {list <map.entry <string, string >> infoids = new ArrayList <pap.entry <string, string >> (paramap.entrySet ()); Coleções.sort (InfOids, novo comparador <map.entry <string, string >> () {public int compare (map.entry <string, string> o1, map.entry <string, string> o2) {return (o1.getKey ()). Tostring (). Compareto (o2.getkey ()); for (int i = 0; i <infoids.size (); i ++) {map.entry <string, string> item = infoids.get (i); //System.out.println (item.getKey ()); if (item.getKey ()! = "") {string key = item.getKey (); String val = item.getValue (); if (urlencode) {val = urlencoder.encode (val, "utf-8"); } buff + = key.tolowercase () + "=" + val + "&"; }} if (buff.isEmpty () == false) {buff = buff.substring (0, buff.length () - 1); }} Catch (Exceção e) {lança nova sdkruntimeException (e.getMessage ()); } retornar buff; } public static boolean isnumeric (string str) {if (str.matches ("// d *")) {return true; } else {return false; }} public static string Arraytoxml (hashmap <string, string> arr) {string xml = "<xml>"; Iterator <entrada <string, string >> iter = arr.entrySet (). Iterator (); while (iter.hasNext ()) {entradas <string, string> entradas = iter.Next (); String key = Entry.getKey (); String val = Entry.getValue (); if (isnumeric (val)) {xml + = "<" + key + ">" + val + "</" + key + ">"; } else xml + = "<" + key + "> <! [CDATA [" + val + "]]> </" + key + ">"; } xml += "</xml>"; tente {return new string (xml.toString (). getBytes (), "ISO8859-1"); } Catch (UnsupportEdEnCodingException e) {// TODO BLOCO DE CATAGEM AUTOGERATO E.PRINTSTACKTRACE (); } retornar ""; }}3.ClientCustomsSl Class de ferramentas , usada para gerar sinais e criar pacote de pedidos weChat com.pay.utils.weixin;
importar java.util.ArrayList; importar java.util.Collections; importar java.util.comparator; importar java.util.hashmap; importar java.util.list; import java.util.map; importar o exemplo. */public class ClientCustomsSl {public static string getBizSign (hashmap <string, string> bizobj) lança sdkruntimeException {hashmap <string, string> bizparameters = new hashmap <string, string> (); List <pap.entry <string, string >> infoids = new ArrayList <pap.entry <string, string >> (bizobj.entrySet ()); System.out.println (infoids); Coleções.sort (InfOids, novo comparador <map.entry <string, string >> () {public int compare (map.entry <string, string> o1, map.entry <string, string> o2) {return (o1.getKey ()). System.out.println (infoids); for (int i = 0; i <infoids.size (); i ++) {map.entry <string, string> item = infoids.get (i); if (item.getKey ()! = "") {bizparameters.put (item.getKey (). tolowerCase (), item.getValue ()); }} //bizparameters.put("Key "," 1234567812345678123456781234567812345671 "); String bizString = Commonutil.FormatBizQueryParamap (BizParameters, false); BizString += "& Key = 123456781234567812345671"; System.out.println ("***************"); System.out.println (BizString); // retorna sha1util.sha1 (BizString); retornar md5util.md5 (BizString); } / ** * WeChat Crie Ordem * @param noncestr * @param orderDescribe * @param orderno * @param preço * @param timeStart * @param timeExpire * @return * @ThKRuntimeException * / public static string CrerenativePackage (string string, string timerDescribe SdkRuntimeException {hashmap <string, string> nativobj = new hashmap <string, string> (); nativobj.put ("Appid", "ver conta pública"); // ID da conta pública nativobj.put ("mch_id", "ver email"); // Número do comerciante nativobj.put ("nonce_str", não -cest); // string aleatória nativobj.put ("corpo", orderDescribe); // Descrição do produto nativobj.put ("Anexar", "Tradeno"); // dados anexados nativobj.put ("out_trade_no", ordem); // Número da ordem do comerciante (global exclusivo) nativobj.put ("total_fee", preço); // O valor total (unidade é central, não pode ser tomado com pontos decimais) nativobj.put ("spbill_create_ip", "192.168.0.144"); // terminal ip nativobj.put ("time_start", timestart); // Time de início da transação nativobj.put ("time_expire", timeExpire); // Time final da transação nativobj.put ("notify_url", personalizadoPropertyPlaceHoldConfigurer.getContextProperty ("wxurl")+"/weixin_callback/weixincallback/init.action"); // Endereço de notificação de retorno de chamada nativobj.put ("trade_type", "nativo"); // Tipo de transação String string = getBizSign (nativoBJ); nativobj.put ("Sign", Sign.ToupPercase ()); return CommonUtil.ArrayToxml (nativoBJ); } /*** Consulta de pagamento do pedido weChat* @param noncestr* @param orderDescribe* @param orderno* @param preço* @param timestart* @param timeExpire* @return* @throws sdkRuntimeException* /public static stringnative stringnative (string transactionIdid, stringsTradeNo OnTrDoStEnO* /public static stringnativePackage (string transactionId, stringTradeNo, stringMoTronscent* /stract) stringnative) String> nativoBJ = novo hashmap <string, string> (); nativobj.put ("Appid", "consulte a conta pública"); // Public Conta IdnativeObj.put ("MCH_ID", "Veja email"); // Número do comerciante nativobj.put ("nonce_str", não -cest); // string aleatória if (! StringUtils.isEmpty (transactionID)) {nativobj.put ("transaction_id", transactionID); } if (! stringUtils.isEmpty (outtradeno)) {nativeObj.put ("out_trade_no", outtradeno); // string aleatória} string signe = getBizSign (nativoBJ); nativobj.put ("signo", sinal.TOUPCASE ()); return CommonUtil.ArrayToxml (nativoBJ); /*** reembolso weChat* @param outtradeno* @param ultleFundno* @param totalfee* @param totalfee* @param reemboltfee* @return* @THKRuntimeException*/public static relefundnativepackage (string outtradeno, string ultrefundNo, stratTothFere, strat, total, strat relefundnativePackage (string outtradeno, string ultrefundNo, strat, total, strat de strathnativepackage (string outtradeno, string ultrefundNo, stractfle {Hashmap <string, string> nativobj = new hashmap <string, string> (); nativobj.put ("Appid", "consulte a conta pública"); // conta pública idnativeObj.put ("mch_id", ver email "); número do níntobj.put (" NINCE_STR ", nonncest); outtradeno); // Número da ordem do comerciante (exclusivo global) nativobj.put ("out_refund_no", alborsefundno); // Número do pedido de reembolso do comerciante (Global único) nativobj.put ("total_fee", totalfee); //, a quantidade total em centavos, os centavos, não pode tomar pontos decimais); centavos, não pode levar pontos decimais) nativobj.put ("op_user_id", "Mail"); String signo = getBizSign (nativoBJ); nativobj.put ("Sign", Sign.TOUPCASE ()); retorna Commonutil.ArrayToxml (nativoBJ);}/*** wechat a ser pago* @param noncestr* @param orderdescribe* @param orderno* @param preço* @param timestart* timeexpire* @param* Createjsapipackage (string não -cest, string orderDescribe, string orderno, preço da string, string timeStart, string timeExpire, string openId) lança sdkruntimeException {hashmap <string, string> nativoBJ = new Hashmap <string, string> (); nativebj.put ("apID." ver public); OpenId); // Public Conta IdnativeObj.put ("MCH_ID", "Ver email") // Número do comerciante nativobj.put ("nonce_str", não -cest); // string aleatório nativobj.put ("Body", OrderDescribe); // Product Descrição Nantobj.put ("Anext", "Tradeno"); Ordemno); // Número da ordem do comerciante (exclusivo global) nativobj.put ("total_fee", preço); // valor total (a unidade é central, não pode levar pontos decimais) nativobj.put ("spbill_create_ip", "192.168.0.144"); // Terminal ipnativeBj.put ("time_time_stert", "time_stert", "time_stert", "time_stert", "timeRtt", "timeRtt"; nativobj.put ("time_expire", timeExpire) // Time de transação Time NativeObj.put ("notify_url", PersonalizadoPropertyPlaceHolderConfigurer.getContextProperty ("wxurl")+"/weixin_callback/weixinclback/init.action); "Jsapi"); // tipo de transação string string = getBizSign (nativoBJ); nativobj.put ("signo", sinal.TOUPPERCASE ()); Retornar CommonUtil.ArrayToxml (nativoBJ);}/*** Feche a ordem no weChat* @param noncestr* @param orderDescribe* @param orderno* @param preços* @param timeStart* @param timeexpire* @param* @return* @ThKRartMeExcender*/Publiciter Strat* SdkruntimeException {hashmap <string, string> nativoBJ = new Hashmap <string, string> (); nativobj.put ("appid", "consulte a conta pública"); // public conta idnativeObj.put ("mch_id", "consulte o email"); // número comercial nativobj.put ("outh); (globalmente exclusivo) nativobj.put ("nonce_str", não -cest); // string string aleatória sinal = getBizSign (nativoBJ); nativobj.put ("signo", sinal.ToupPercase ()); Retornar Commonutil.ArrayToxml (nativoBJ);}} 4. Ligue para a interface de pagamento do WeChat
pacote com.pay.controller.weixin; importar java.io.file; importar java.io.fileInputStream; importar java.security.keystore; import java.text.simpledFormat; import java.util.date; import java.util.list; javax.servlet.http.httpServletReQuest; importar javax.servlet.http.httpServletResponse; importação net.sf.json.jsonArray; importação net.sf.json.jsonObject; import org.apache.http.httpentity; org.apache.http.client.methods.closablehttpResponse; importar org.apache.http.client.methods.httppost; importar org.apache.http.conn.ssl.slconningsFactory; importCexToxToxTox.Sl.Conn.SSL.SSL.SSLECKENSKETCORTION; importConxExToxTeCache.http.conn.ssl.slconnocketFactory; importação de orgache.http.conn.ssl.sslocketFactory; org.apache.http.entity.stringentity; importar org.apache.http.impl.client.closeablehttpclient; importar org.apache.http.impl.client.httpclients; importar org.httpach.util.entityutils; org.dom4j.documentHelper; importar org.dom4j.Element; importar org.dom4j.io.saxReader; importar org.springframework.beans.factory.annotation.autowired; importação org.springframework.http.httttTus; org.springframework.web.bind.annotation.requestmapping; importar org.springframework.web.bind.annotation.requestmethod; importação org.springframework.web.bind.annotation.restonsentsind; com.pay.bo.payhistants.import com.pay.constants.payhistoryPayStatus; import com.pay.constants.payhistoryPayType; importar com.pay.service.weixInPayService; import com.pay.utils.weixin.clientMsl; com.pay.utils.weixin.closeweixinorderutils; importar com.pay.utils.weixin.customizedPropertyPlaceHoldConfigurer;@RestController@requestmapp ("/pay") classe pública WeixInPay {@autowired Padrão de longo prazo estático privado = 1662652800000L; /** * Retorne o URL que gera o código QR * @param request * @param Resposta * @return */@requestMapping (value = "/geturl", método = requestmethod.Post) @ResponsEstatus (httpstatus.ok) objeto público getUrl (hTTPSETROPSOnsion JsonObject.FromObject (corpo); Payhist ph = null; // list <map <string, objeto >> td = weixInPayService.gettrade (orderno); Data dt = new Date (); SimpledateFormat sdf = new SimpleDateFormat ("yyyymmddhhmmss"); String noncestr = sdf.format (dt) .toString (); Data agora = new date (); String tradePayno = jsono.get ("orderno"). Tostring ()+string.format ("%10d", standardTime - agora.gettime ()). Substring (0, 10); System.out.println ("订单标号 orderno =======" "+jsono.get (" orderno "). Tostring ()); System.out.println ("10 位随机数 ======="+string.format ("%10d", StandardTime - agora.getTime ()). Substring (0, 10)); Preço da String = Math.Round (float.valueof (jsono.get ("Price"). ToString ())*100)+""; Long timeexpirestrold = dt.gettime (); Timenew long timenew = long.parselong (PersonalizadoPropertyPlateHoldConfigurer.getContextProperty ("weixin.send2finish.overtime"). Tostring ()); Longo timeexpirenew = timeExpirestrold+timenew; Data dttimeExpire = nova data (TimeExpirenew); SimpledateFormat dtsdf = new SimpleDateFormat ("yyyymmddhhmmss"); String timeExpire = dtsdf.format (dttimeExpire) .toString (); System.out.println ("noncestr =="+não -cest); System.out.println ("orderno =="+jsono.get ("Orderno"). ToString ()); System.out.println ("Price =="+Price); System.out.println ("timeStart =="+não -cest); System.out.println ("timeExpire =="+timeExpire); Resultado JsonObject = (JsonObject) Seturl (Non -Cest, "Order", TradePayno, Price, Non -Cest, TimeExpire); if (resultado.get ("status"). ToString (). Equals ("Success")) {ph = new Payhist (); Ph.SetTradePayurl (Result.GetString ("WeixInPayurl")); // Este campo é um link de pagamento. Você pode gerar um código QR para digitalizar o código para pagar Ph.SetPayTradeno (JSONO.GET ("Orderno"). ToString ()); Ph.SetTradePayno (TradePayno); Ph.SetPayStatus (payhistorypaystatus.wechat_pay_status_wait); Ph.SetPayType (payhistoryPaytype.wechat); Ph.SetAppKey (JSONO.GetString ("AppKey"). ToString ()); Ph.SetPayamount (preço); resultado.put ("paytradeno", ph.getpaytradeno ()); resultado.put ("tradePayno", ph.getTradePayno ()); resultado.put ("paystatus", ph.getPayStatus ()); resultado.put ("PayType", Ph.getPayType ()); } resultado de retorno; } catch (Exceção e) {e.printStackTrace (); Resultado jsonObject = new jsonObject (); resultado.put ("status", "erro"); resultado.put ("msg", e.getMessage ()); // return result.toString (); } retornar nulo; } public Object Seturl (String Non -Cest, String OrderDescribe, String OrderNo, String Preço, String TimeStart, String timeExpire) {try {keystore keystore = keystore.getInstance ("pkcs12"); FileInputStream Instruam = new FileInputStream (novo arquivo (caminho absoluto do certificado WeChat)); tente {keystore.load (Instream, "Merchant ID" .ToCharArray ()); } finalmente {EnterStream.close (); } // Trust CA próprio CA e todos os certificados autoassinados sslContext sslContext = sslContexts.custom (). Loadkeymaterial (keystore, <span style = "font-family: arial, helvetica, sans-serif;"> mercante id </span> .ToCharArray ()). // Permitir apenas o protocolo TLSV1 SSLConnectionSocketFactory SSLSF = new SSLConnectionSocketFactory (SSLContext, new String [] {"tlsv1"}, null, sslConnectionSocketFactory.allow_all_Hostname_Verifier); ClosableHttpClient httpClient = httpClients.custom () .SetsSlSocketFactory (sslsf) .build (); // httpget httpget = new // httpget ("https://api.mch.weixin.qq.com/secapi/pay/refund"); Httppost httppost = new httppost ("https://api.mch.weixin.qq.com/pay/unifiedorder"); String xml = clientcustomssl.createnativepackage (não -cest, orderDescribe, orderno, preço, timeStart, timeExpire); tente {stringentity se = new stringentity (xml); httppost.setentity (SE); System.out.println ("Execução da solicitação" + httppost.getRequestLine ()); ClosableHttpResponse ResponderEntry = httpClient.execute (httppost); tente {httpentity entity = respostaEntry.getEntity (); System.out.println ("--------------------------------------"); System.out.println (ResponseEntry.getStatusline ()); if (entidade! = null) {System.out.println ("Resposta Conteúdo Comprimento:" + entity.getContentLength ()); /* Bufferredreader bufferreader = new buffarreder (new InputStreamReader (entity.getContent ())); Texto de string; while ((text = bufferredreader.readline ()))! = null) {System.out.println ("=================="+Texto); }*/ Saxreader saxReader = new saxReader (); Documento documento = saxreader.read (entity.getContent ()); Elemento rootelt = document.getrootelement (); System.out.println ("nó raiz:" + rootelt.getName ()); System.out.println ("==="+rootelt.ElementText ("result_code")); System.out.println ("==="+rootelt.ElementText ("return_msg")); String resultCode = rootelt.ElementText ("result_code"); Resultado jsonObject = new jsonObject (); DocumentXML = DocumentHelper.ParseText (XML); Elemento rooteltxml = documentxml.getrootelement (); if (resultadocode.equals ("succcess")) {System.out.println ("================== Prébio pré -PAY_ID ====================="+ Rootelt.ElementText ("PrePay_id_id"); System.out.println ("================= SIGN ====================="+ rooteltxml.ElementText ("sinal")); resultado.put ("weixinpayurl", rootelt.ElementText ("code_url")); resultado.put ("pré -anyid", rootelt.ElementText ("pré -any_id")); resultado.put ("status", "sucesso"); resultado.put ("msg", "sucesso"); } else {result.put ("status", "false"); resultado.put ("msg", rootelt.ElementText ("err_code_des")); } resultado de retorno; } Entityutils.consume (entidade); } finalmente {ResponseEntry.close (); }} finalmente {httpclient.close (); } retornar nulo; } catch (Exceção e) {e.printStackTrace (); Resultado jsonObject = new jsonObject (); resultado.put ("status", "erro"); resultado.put ("msg", e.getMessage ()); resultado de retorno; }}} pacote httpclient jar e pacote json jar: endereço de download.
O exposto acima é todo o conteúdo deste artigo. Espero que seja útil para o aprendizado de todos e espero que todos apoiem mais o wulin.com.