Recientemente, he estado expuesto a algunas cosas sobre el pago de WeChat en mi trabajo. Vi que las demostraciones que di eran todas las versiones de PHP, y realmente no estaba satisfecho con los documentos de pago de WeChat. Después de experimentar muchas trampas, estaba inactivo para hacer un resumen.
1. Preparación
Para desarrollar WeChat, primero debe solicitar una cuenta pública. Después de que la solicitud sea exitosa, se le enviará por correo electrónico. La cuenta pública contiene documentos de desarrollo, información necesaria durante el desarrollo y consulta de datos para las pruebas.
2. Herramientas
1. Clase de herramienta de cifrado de MD5
paquete com.pay.utils.weixin; import 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'}; intente {byte [] btinput = s.getBytes (); // Obtenga el objeto MessageDigest del algoritmo Digest MD5 MessageGest Mdinst = MessageDigest.getInstance ("MD5"); // actualizar el digest mdinst.update (btinput); // Obtener el byte de texto cifrado [] md = mdinst.digest (); // Convierta el texto cifrado en un formulario de cadena HEX int j = md.length; char str [] = nuevo char [j * 2]; int k = 0; para (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 (); regresar nulo; }}}2. Clase de herramienta CommonUtil , utilizada para reemplazar el XML requerido para WeChat. La siguiente cadena de retorno nueva (xml.ToString (). GetBytes (), "ISO8859-1"); Cambie UTF-8 en la clase de herramientas a ISO8859-1, de lo contrario, el texto chino en el orden WeChat aparecerá confuso, y se puede mostrar correctamente después del cambio.
paquete com.pay.utils.weixin; import java.io.unsupportedenCodingException; import java.net.urlencoder; import java.util.*; import java.util.map.entry; clase pública Commonutil {public static string createenCestr (int longitud) {cadena de cadena = = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; Cadena res = ""; for (int i = 0; i <longitud; i ++) {random rd = new Random (); res += chars.indexof (rd.nextint (chars.length () - 1)); } return Res; } public static string createenOnCestr () {string chars = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789"; Cadena res = ""; for (int i = 0; i <16; i ++) {random rd = new Random (); res += charscharat (rd.nextint (chars.length () - 1)); } return Res; } public static string formateQueryParamap (hashmap <string, string> parámetros) lanza sdkrunteException {string buff = ""; Pruebe {list <map.entry <string, string >> infoids = new ArrayList <map.entry <string, string >> (parameters.entryset ()); Colección.sort (infoids, nuevo comparador <map.entry <string, string >> () {public int Compare (map.entry <string, string> o1, map.entry <string, string> o2) {return (o1.getkey ()). ToString (). Comparación (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 (Exception e) {tire nuevo sdkrunteException (e.getMessage ()); } Buff de retorno; } public static string formatBizQueryParamap (hashmap <string, string> paramap, boolean urlencode) lanza sdkrunteException {string buff = ""; Pruebe {list <map.entry <string, string >> infoids = new ArrayList <map.entry <string, string >> (paramap.entryset ()); Colección.sort (infoids, nuevo comparador <map.entry <string, string >> () {public int Compare (map.entry <string, string> o1, map.entry <string, string> o2) {return (o1.getkey ()). ToString (). Comparación (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 (); Cadena 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 (Exception e) {tire nuevo sdkrunteException (e.getMessage ()); } Buff de retorno; } 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 <Entry <String, String >> iter = arr.entryset (). Iterator (); while (iter.hasnext ()) {Entry <String, String> Entry = iter.next (); Clave de cadena = Entry.getKey (); Cadena val = entry.getValue (); if (isNumeric (val)) {xml + = "<" + key + ">" + val + "</" + key + ">"; } else xml + = "<" + key + "> <! [Cdata [" + val + "]]> </" + key + ">"; } xml += "</xml>"; intente {return new String (xml.ToString (). getBytes (), "ISO8859-1"); } Catch (UnsupportedEnCodingException e) {// TODO Auto Generado Bloque E.PrintStackTrace (); } devolver ""; }}3.ClientCustomSsl Class de herramientas , utilizada para generar señales y crear el paquete de pedido de WeChat com.pay.utils.weixin;
import java.util.arrayList; import java.util.collections; import java.util.comparator; import java.util.hashmap; import java.util.list; import java.util.map; import org.springFrframe.util.stringutilss;/** * Este ejemplo demuestra cómo crear contexciones seguras con un contexto de costumbres. */public class ClientCustomsSl {public static string getBizsign (hashmap <string, string> bizobj) lanza sdkrunteException {hashmap <string, string> bizParameters = new HashMap <String, String> (); List <map.entry <string, string >> infoids = new ArrayList <map.entry <string, string >> (bizobj.entryset ()); System.out.println (infoides); Colección.sort (infoids, nuevo comparador <map.entry <string, string >> () {public int Compare (map.entry <string, string> o1, map.entry <string, string> o2) {return (o1.getKey ()). ToString (). Comparación (o2.getkey ());}); system.println ("---------------"); System.out.println (infoides); 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, falso); BizString += "& Key = 123456781234567812345671"; System.out.println ("***************"); System.out.println (bizString); // return sha1util.sha1 (bizString); return md5util.md5 (bizString); } / ** * wechat crea orden * @param noncestr * @param ordendescribe * @param ordenno * @param precio * @param timestart * @param timeExpire * @return * @throws sdkrunteException * / public static string createnativePackage (string noncestr, string noncon tira sdkruntimeException {hashmap <string, string> nationalObj = new HashMap <String, String> (); nationalObj.put ("Appid", "Ver cuenta pública"); // Cuenta pública id nationalObj.put ("mch_id", "ver correo electrónico"); // número comercial nationalObj.put ("nonce_str", noncestr); // cadena aleatoria nationalObj.put ("Body", OrderDescribe); // Descripción del producto nationalObj.put ("adjuntar", "tradeno"); // datos adjuntos nationObj.put ("out_trade_no", ordenno); // número de orden comercial (global único) nationalObj.put ("Total_fee", precio); // cantidad total (la unidad es centava, no se puede tomar con puntos decimales) nationalObj.put ("spbill_create_ip", "192.168.0.144"); // terminal IP nationalObj.put ("Time_start", timStart); // Tiempo de inicio de transacción nationalObj.put ("Time_Eppire", TimeExpire); // Tiempo de finalización de transacción nationalObj.put ("notify_url", personalizedPropertyPlaceHolderConfigurer.getContextProperty ("wxurl")+"/weixin_callback/weixincallback/init.action"); // dirección de notificación de devolución de llamada nationalObj.put ("Trade_Type", "nativo"); // Tipo de transacción String firma = getBizsign (nationalBj); nationalObj.put ("firmar", sign.touppercase ()); return CommonUtil.ArrayToxml (nationalBj); } /*** WeChat Order Page Consuly* @Param Noncestr* @param OrderDescribe* @param Orderno* @param Price* @param timeStarSt* @param timeExpire* @return* @throws sdkrunteException* /public stringnativePackage (string transaccionId, string outtradeNe {Hashmap <String, String> nationalObj = new Hashmap <String, String> (); nationalObj.put ("Appid", "Ver cuenta pública"); // Cuenta pública idnativeBj.put ("mch_id", "ver correo electrónico"); // número comercial nationalObj.put ("nonce_str", noncestr); // cadena aleatoria if (! StringUtils.isEmpty (transaccionId)) {nationalObj.put ("transaccion_id", transaccionId); } if (! StringUtils.isEmpty (outtradeno)) {nationalObj.put ("out_trade_no", outtradeno); // String aleatoria} String firma = getBizSign (nationalBj); nationalObj.put ("signar", sign.touppercase ()); return CommonUtil.ArrayToxml (nationalBj); /*** reembolso wechat* @param outtradeno* @param outfundno* @param totalfee* @param totalfee* @param reembols SdkruntimeException {hashmap <string, string> nationObj = new Hashmap <String, String> (); nationalObj.put ("Appid", "Ver cuenta pública"); // Cuenta pública IdnativeBj.put ("Mch_id", "Ver correo electrónico"); // Número de comerciante nationalObj.put ("out_trade_no", outtradeno); // número de orden comercial (global único) nationalObj.put ("out_refund_no", outfundno); // Número de orden de reembolso de comerciante (global único) nationalBj.put ("total_fee", total de total); // cantidad (unidad en los centros no puede tomar decimal puntos) national) reembolso); // Cantidad de reembolso (unidad en centavos, no puede tomar puntos decimales) nationalBj.put ("op_user_id", "correo"); String Sign = GetBizsign (nationalBj); nationalObj.put ("firmar", sign.touppercase ()); return CommonUtil.ArrayToxMl (nationalObj);}/*** wechat a ser pagado* @param noncestr* @param ordendescribe* @param ordenno* @param precio* aparam timestoT* @param timexpire* @return* @throws sdkruntrunteN CreateJsapIpackage (String Non -Cestr, String OrderDescribe, String OrderNo, String Price, String TimeStart, String TimeExpire, String OpenId) lanza SDKRUNTEMEEXCECECION {Hashmap <String, String> nationalBj = new HashMap <String, String> (); nationalBj.put ("AppID", "ver cuenta pública"); // pública de cuenta pública <shMap <String, ");" APERTER ("APRICACIÓN". OpenId); // Cuenta pública idnativeBj.put ("mch_id", "ver correo electrónico") // número comercial nationalObj.put ("nonce_str", noncestr); // string aleator ordenno); // Número de orden comercial (global único) nationalObj.put ("total_fee", precio); // Total Canting (Unidad es Cent, no puede tomar puntos decimales) nationalObj.put ("Spbill_create_ip", "192.168.0.144"); // Terminal IpnativeBj.put ("Time_Start", Timestarart); nationalObj.put ("Time_Eptire", TimeExpire) // Transaction End Time nationObj.put ("notify_url", personalizedPropertyplaceHolderConfigurer.getContextProperty ("wxurl")+"/weixin_callback/weixincallback/init.action"); // Notificación de la dirección national. "JSAPI"); // Tipo de transacción String Sign = GetBizsign (nationalBj); nationalObj.put ("firmar", sign.toupperCase ()); return CommonUtil.ArrayToXml(nativeObj);}/*** Close order on WeChat* @param nonceStr* @param orderDescribe* @param orderNo* @param price* @param timeStart* @param timeExpire* @param openId* @return* @throws SDKRuntimeException*/public static String CreateCloseOrder(String outTradeNo,String nonceStr) tira sdkrunteException {hashmap <string, string> nationalObj = new HashMap <String, String> (); nationalBj.put ("Appid", "ver la cuenta pública"); // cuenta pública idnativeBj.put ("mch_id", "ver el correo electrónico")); // comerciante nationObj.put ("out_trade_no", outtradene; (globalmente único) nationalObj.put ("nonce_str", noncestr); // cadena aleatoria de cadena sign = getBizsign (nationalObj); nationalObj.put ("firma", sign.touppercase ()); return CommonUtil.ArrayToxml (nationalBj);}} 4. Llame a la interfaz de pago de WeChat
paquete com.pay.controller.weixin; import java.io.file; import java.io.fileInputStream; import java.security.keystore; import java.text.simpledateFormat; import java.util.date; import java.util.list; list; javax.servlet.http.httpservletRequest; import javax.servlet.http.httpservletResponse; import net.sf.json.jsonarray; import net.sf.json.jsonObject org.apache.http.client.methods.closEablehttTpesponse; import org.apache.http.client.methods.httppost; import org.apache.http.conn.ssl.sslconnectionsockyfactory; import org.apache.http.conn.ssl.sslcontexts; import org.apache.http.entity.StringEntity; importar org.apache.http.impl.client.ClosEableHttpClient; importar org.apache.http.impl.client.httpclients; import org.http.util.eNTITILLS; import org.dom4j.document; import; org.dom4j.documentHelper; import org.dom4j.element; import org.dom4j.io.saxreader; import org.springframework.beans.factory.annotation.aUtowired; import og.springframework.htttttttpstatus; import org.springframe.wewwork.htntnby; org. com.pay.bo.payhistants.import com.pay.constants.payhistorypaystatus; import com.pay.constants.payhistorypaytype; import com.pay.service.weixinpayservice; import com.pay.utils.weixin.clientcustomssl; com.pay.utils.weixin.customizedPropertyplaceHolderConfigurer;@RestController@requestMapping ("/pay") clase pública weixInpayController {@aUtowired weixinpayservice weixinpayservice; Standard Standard, estática privada = 1662652800000L; /** * Devuelve la URL que genera el código QR * @param solicitud * @param respuesta * @return */@RequestMapping (valor = "/getUrl", método = requestmethod.post) @ResponseStatus (httpstatus.ok) Public Object (httpServletResponse Response, @requestbody string corporal) {try {jsonObject Jsono = JsonObject.FromObject (cuerpo); PayHist ph = null; // list <map <string, object >> td = weixInpayservice.getTrade (ordenno); Fecha dt = nueva fecha (); SimpleDateFormat sdf = new SimpleDateFormat ("yyyymmddhhmmss"); Cadena non -Cestr = sdf.format (dt) .ToString (); Fecha ahora = nueva fecha (); String tradepayNo = jsono.get ("ordenno"). ToString ()+string.format ("%10d", StandardTime - Now.getTime ()). Substring (0, 10); System.out.println ("订单标号 OrderNo ========"+Jsono.get ("Orderno"). ToString ()); System.out.println ("10 位随机数 ========"+String.Format ("%10d", StandardTime - Now.getTime ()). Subcepting (0, 10)); String Price = Math.round (float.valueOf (jsono.get ("precio"). ToString ())*100)+""; Long TimeExpirestrold = dt.gettime (); Long timeNew = long.parselong (CustomizedPropertyPlaceHolderConfigurer.getContextProperty ("weixin.send2finish.overtime"). ToString ()); Long TimeExpireWe = TimeExpirestrold+TimeNew; Fecha dttimeExpire = nueva fecha (TimeExpireNew); SimpleDateFormat dtsdf = new SimpleDateFormat ("yyyymmddhmmss"); String TimeExPire = dtsdf.format (dttimeExpire) .ToString (); System.out.println ("non -Cestr =="+Non -Cestr); System.out.println ("ordenno =="+jsono.get ("ordenno"). ToString ()); System.out.println ("Price =="+Price); System.out.println ("timStart =="+no -CESTR); System.out.println ("TimeExpire =="+TimeExpire); JSONOBJECT result = (jsonObject) seturl (no cocestido, "orden", tradepayno, precio, non -coc, timeExpire); if (resultado.get ("status"). toString (). Equals ("éxito")) {ph = new PayHist (); ph.setTradepayUrl (result.getString ("weixinpayurl")); // Este campo es un enlace de pago. Puede generar un código QR para escanear el código para pagar ph.setPayTradeno (jsono.get ("ordenno"). ToString ()); ph.SetTradepayno (TradEpayNo); Ph.SetPayStatus (PayHistoryPayStatus.wechat_pay_status_wait); ph.setPayType (PayHistoryPayType.wechat); ph.setAppkey (jsono.getString ("appKey"). toString ()); ph.setPayAMunt (precio); resultado.put ("PayTradeno", ph.getpaytradeno ()); resultado.put ("TradePayno", ph.getTradepayno ()); resultado.put ("PayStatus", Ph.getPayStatus ()); resultado.put ("PayType", ph.getPayType ()); } resultado de retorno; } catch (Exception e) {E.PrintStackTrace (); JsonObject resultado = new JsonObject (); resultado.put ("estado", "error"); resultado.put ("msg", e.getMessage ()); // return dente.ToString (); } return null; } Public Object seturl (String Non -Cestr, String OrderDescribe, String OrderNo, String Price, String TimeStart, String TimeExpire) {try {KeyStoreStore KeyStore = KeyStore.getInstance ("PKCS12"); FileInputStream Insteam = new FileInputStream (nuevo archivo (ruta absoluta del certificado WeChat)); intente {KeyStore.Load (INSTAÑO, "ID de comerciante" .ToCarArray ()); } finalmente {enterTream.close (); } // Confía en su propio CA y todos los certificados autofirmados sslContext sslContext = sslContexts.custom (). LoadKeMaterial (KeyStore, <span style = "Font-Family: Arial, Helvetica, Sans-Serif;"> ID de comerciante </span> .toCarArray ().); // Permitir el protocolo tlsv1 solo sslconnectionsocketFactory sslsf = new sslConnectionsocketFactory (sslContext, new String [] {"tlsv1"}, null, sslconnectionsocketfactory.how_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 (Non -Cestr, OrderDescribe, OrderNo, Price, TimeStart, TimeExPire); intente {StringEntity SE = New StringEntity (xml); httppost.setEntity (SE); System.out.println ("Ejecución de solicitud" + httppost.getRequestline ()); ClosableHttPResponse ResponseEntry = httpclient.execute (httppost); intente {httpentity entity = ResponseEntry.getEntity (); System.out.println ("----------------------------------------"); System.out.println (ResponseEntry.getStatusLine ()); if (entity! = null) {System.out.println ("Longitud de contenido de respuesta:" + entity.getContentLength ()); /* BufferedReader BufferedReader = new BufferedReader (new InputStreamReader (entity.getContent ())); Texto de cadena; while ((text = bufferedReader.readline ()))! = null) {system.out.println ("==================="+Text); }*/ Saxreader saxreader = new saxreader (); Documento documento = saxreader.read (entity.getContent ()); Elemento rootelt = document.getRootElement (); System.out.println ("nodo raíz:" + rootelt.getName ()); System.out.println ("==="+rootelt.elementText ("result_code")); System.out.println ("==="+rootelt.elementText ("return_msg")); Cadena resultCode = rootElt.ElementText ("result_code"); JsonObject resultado = new JsonObject (); Documento documentxml = documenthelper.parsetext (xml); Elemento rootEltxml = documentxml.getRootElement (); if (resultcode.equals ("éxito")) {System.out.println ("================== PREPAY_ID ====================="+ Rootelt.elementText ("Preparay_id")); System.out.println ("================= Sign ====================="+ RootEltxml.ElementText ("signo")); resultado.put ("weixinpayurl", rootelt.elementText ("code_url")); resultado.put ("prepayId", rootElt.ElementText ("prepay_id")); resultado.put ("estado", "éxito"); resultado.put ("msg", "éxito"); } else {result.put ("status", "false"); resultado.put ("msg", rootelt.elementText ("err_code_des")); } resultado de retorno; } EntityUtils.consume (entidad); } Finalmente {ResponseEntry.Close (); }} finalmente {httpclient.close (); } return null; } catch (Exception e) {E.PrintStackTrace (); JsonObject resultado = new JsonObject (); resultado.put ("estado", "error"); resultado.put ("msg", e.getMessage ()); resultado de retorno; }}} paquete jar httpclient y paquete json jar: descargar dirección.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.