1. Persiapan
Pertama -tama, mari kita mengeluh tentang pembayaran WeChat. Ada beberapa model pembayaran yang didukungnya, tetapi dokumen resmi khususnya tersebar, dan bahkan tidak ada beberapa demo terkait Java yang layak. Saya belum pernah melakukan pembayaran weChat sebelumnya. Saya benar -benar terpana olehnya di awal. Saya akhirnya berhasil selama dua hari. Saya menulisnya di sini untuk menikmati generasi mendatang!
Mengenai pekerjaan persiapan, alamat dokumen resmi "WeChat Scan QR Code Payment Model 2" ada di sini https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1 Anda dapat melihatnya terlebih dahulu. Faktanya, ada hal -hal berikut untuk disiapkan:
Di antara mereka, App_id dan App_secret dapat ditemukan di platform publik, sementara MCH_ID dan API_Key ditemukan di platform pedagang. Secara khusus, API_Key harus diatur pada platform pedagang. Untuk "WeChat Scan Code Payment Mode 2" (pembayaran dan panggilan balik), hanya App_ID, MCH_ID dan API_Key yang akan digunakan, dan tidak ada lagi yang digunakan.
Saya tidak akan berbicara tentang lingkungan pengembangan. Apakah Anda SpringMVC, Struts2, atau Direct Serverlet, itu hampir sama. Selama Anda dapat memastikan bahwa metode yang sesuai dapat dipanggil. Mengenai mengutip paket toples pihak ketiga, saya hanya menggunakan JDOM yang mengoperasikan XML di sini. Ingatlah bahwa ini adalah versi 1.*, bukan 2.* terbaru di situs web resmi, dan keduanya tidak kompatibel. Secara khusus, itu adalah JDOM-1.1.3.jar, paket ketergantungan Jaxen-1.1.6.jar. Untuk dua paket ini, saya tidak menggunakan httpClient yang digunakan dalam beberapa contoh. Rasanya tidak perlu, dan paket ketergantungannya sangat rumit. Tentu saja, Anda adalah Maven ketika saya tidak mengatakannya.
2. Pengembangan dan pertempuran praktis
1. Pertama, sambungkan ke antarmuka WeChat dan dapatkan kode QR pembayaran WeChat.
string publik weixin_pay () melempar pengecualian {// informasi akun string appid = payconfigutil.app_id; // appid // string appsecret = payconfigutil.app_secret; // appsecret string mch_id = payconfigutil.mch_id; // Nomor Bisnis Kunci string = payconfigutil.api_key; // Key String Currtime = payCommonutil.getCurrtime (); String strtime = Currtime.substring (8, Currtime.length ()); String strrandom = paycommonutil.buildrandom (4) + ""; String nonce_str = strtime + strrandom; String order_price = 1; // CATATAN HARGA: Unit harga dibagi menjadi body string = "Goodsssssss"; // nama produk string out_trade_no = "11338"; // Nomor pesanan // Dapatkan Inisiasi Komputer IP String spbill_create_ip = payconfigutil.create_ip; // string antarmuka callback notify_url = payconfigutil.notify_url; String trade_type = "asli"; 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 ("body", body); 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 = paycommoneTil.createSign ("UTF-8", PackageParams, Key); packageParams.put ("tanda", tanda); String requestXml = paycommonutil.getRequestXml (packageParams); System.out.println (RequestXml); String resxml = httputil.postdata (payconfigutil.ufdoder_url, requestXml); Peta peta = 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"); mengembalikan URLCode; }Jika tidak ada yang tidak terduga terjadi, kami mendapatkan URL pembayaran dari server WeChat, yang terlihat seperti WEIXIN: // WXPAY/BIZPAYURL? PR = PIXXXXX. Setelah itu, kita perlu menghasilkan kode QR untuk URL ini, dan kemudian kita dapat menggunakan ponsel kita WeChat untuk memindai kode yang harus dibayar. Ada banyak cara untuk menghasilkan kode QR. Harap mengambil kebutuhan Anda sendiri. Saya menyediakan antarmuka pembuatan kode Google QR di sini:
string statis publik qrfromgoOgle (string chl) melempar pengecualian {int widhtheight = 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; mengembalikan qrfromgoogle; } // Karakter Khusus Pemrosesan Urlencode String Statis Public (String SRC) melempar UnsportedencodingException {return urlencoder.encode (SRC, "UTF-8"). Ganti ("+", "%20"); } Kode di atas melibatkan beberapa kelas alat: payconfigutil, paycommonutil, httputil dan xmlutil. Di antara mereka, PayConfigutil menempatkan beberapa konfigurasi dan jalur yang disebutkan di atas. PayCommonutil melibatkan beberapa metode untuk mendapatkan peristiwa saat ini, menghasilkan string acak, mendapatkan tanda tangan parameter dan menyambung XML. Kodenya adalah sebagai berikut:
PayCommonutil kelas publik { /*** Apakah tanda tangannya benar, aturannya adalah: mengurutkan berdasarkan nama parameter AZ, dan parameter yang menghadapi nilai kosong tidak berpartisipasi dalam tanda tangan. * @return boolean */ public static boolean iStenpaySign (string characterencoding, sortedmap <objek, objek> packageParams, string api_key) {stringBuffer sb = stringBuffer baru (); Set es = packageParams.entryset (); Iterator it = es.iterator (); while (it.hasnext ()) {map.entry entri = (map.entry) it.next (); String k = (string) entri.getKey (); String v = (string) entry.getValue (); if (! "tanda" .Equals (k) && null! = v &&! " }} sb.append ("key =" + api_key); // File ringkasan string mySign = md5util.md5Encode (sb.tostring (), karakterencode) .tolowercase (); String TenPaySign = ((String) PackageParams.get ("Sign"). TolowerCase (); //System.out.println(tenPaySign + "" + mySign); kembalikan TenPaySign.Equals (MySign); } /** * @author * @date 2016-4-22 * @Description: sign signature* @param characterEncoding * Encoding format* @param parameters * Request parameters* @return */ public static String createSign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) { StringBuffer sb = new StringBuffer(); Set es = packageParams.entryset (); Iterator it = es.iterator (); while (it.hasnext ()) {map.entry entri = (map.entry) it.next (); String k = (string) entri.getKey (); String v = (string) entry.getValue (); if (null! = v &&! "". Equals (v) &&! "Sign" .equals (k) &&! "key" .equals (k)) {sb.append (k + "=" + v + "&"); }} sb.append ("key =" + api_key); String Sign = md5util.md5Encode (sb.toString (), characterencoding) .touppercase (); tanda kembali; } /** * @author * @date 2016-4-22 * @Description: Convert request parameters to xml format string * @param parameters * Request parameters * @return */ public static String getRequestXml(SortedMap<Object, Object> parameters) { StringBuffer sb = new StringBuffer(); SB.Append ("<xml>"); Set es = parameter.entryset (); Iterator it = es.iterator (); while (it.hasnext ()) {map.entry entri = (map.entry) it.next (); String k = (string) entri.getKey (); String v = (string) entry.getValue (); if ("lampirkan" .equalsignorecase (k) || "body" .equalsignorecase (k) || "tanda" .equalSignorecase (k)) {sb.append ("<" + k + ">" + "<! [cdata [" + v + "]]> </" + K + ">"); } else {sb.append ("<" + k + ">" + v + "</" + k + ">"); }}} sb.append ("</xml>"); return sb.tostring (); } /*** Keluarkan bilangan bulat positif acak dari ukuran panjang yang ditentukan. * * @param length * int mengatur panjang nomor acak yang diambil. Panjang kurang dari 11 * @return int mengembalikan nomor acak yang dihasilkan. */ public static int buildrandom (panjang int) {int num = 1; double random = math.random (); if (acak <0,1) {acak = acak + 0,1; } untuk (int i = 0; i <panjang; i ++) {num = num * 10; } return (int) ((acak * num)); } / ** * Dapatkan waktu saat ini yyyymmddhhmmss * * @return string * / string statis public getCurrtime () {date now = new date (); SimpleDateFormat outFormat = new SimpleDateFormat ("yyyymmddhhmmss"); String s = outformat.format (sekarang); kembali S; }}Httputil dan xmlutil adalah sebagai berikut:
kelas publik httputil {private static final log logger = logs.get (); private final static int connect_timeout = 5000; // dalam string statis final privat jutaan orang 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) {buferedReader reader = null; coba {url url = url baru (urlstr); Urlconnection conn = url.openconnection (); Conn.SetDoOutput (true); conn.setConnectTimeOut (connect_timeout); conn.setreadtimeout (connect_timeout); if (contentType! = null) conn.setRequestProperty ("tipe konten", contentType); OutputStreamWriter writer = outputStreamWriter baru (conn.getoutputStream (), default_encoding); if (data == null) data = ""; writer.write (data); writer.flush (); writer.close (); pembaca = BufferedReader baru (inputStreamReader baru (conn.getInputStream (), default_encoding)); StringBuilder SB = StringBuilder baru (); Garis string = null; while ((line = reader.readline ())! = null) {sb.append (line); SB.Append ("/r/n"); } return sb.toString (); } catch (ioException e) {logger.error ("kesalahan yang menghubungkan ke" + urlstr + ":" + e.getMessage ()); } akhirnya {coba {if (reader! = null) reader.close (); } catch (ioException e) {}} return null; }} Kelas Publik Xmlutil { /*** Parse XML dan kembalikan pasangan nilai kunci elemen tingkat pertama. Jika elemen tingkat pertama memiliki anak, nilai node ini adalah data XML dari simpul anak. * @param strxml * @return * @throws jdomexception * @throws ioException */public peta statis doxmlparse (string strxml) melempar jdomexception, ioException {strxml = strxml.replacefirst ("encoding =/". " if (null == strxml || "" .Equals (strxml)) {return null; } Peta m = hashMap baru (); InputStream in = new BytearRayInputStream (strxml.getBytes ("UTF-8")); Saxbuilder Builder = Saxbuilder baru (); Dokumen dokumen = builder.build (dalam); Elemen root = doc.getrooteLement (); Daftar daftar = root.getchildren (); Iterator it = list.iterator (); while (it.hasnext ()) {elemen e = (elemen) it.next (); String k = e.getName (); String v = ""; Daftar anak -anak = e.getchildren (); if (children.isempty ()) {v = e.gettextNormalize (); } else {v = xmlutil.getChildRentext (anak -anak); } m.put (k, v); } // tutup aliran di.close (); kembali m; } / ** * Dapatkan xml dari node anak * @param anak -anak * @return string * / public static string getChildRentext (daftar anak -anak) {stringBuffer sb = stringBuffer baru (); if (! children.isempty ()) {iterator it = children.iterator (); while (it.hasnext ()) {elemen e = (elemen) it.next (); String name = e.getName (); Nilai string = e.gettextNormalize (); Daftar daftar = e.getchildren (); SB.Append ("<" + Name + ">"); if (! list.isempty ()) {sb.append (xmlutil.getchildrentext (list)); } SB.Append (nilai); SB.Append ("</" + Name + ">"); }} return sb.toString (); }} Tentu saja ada juga kelas alat komputasi MD5
kelas publik md5Util {private static string bytearraytoHexString (byte b []) {stringBuffer resultB = new stringBuffer (); untuk (int i = 0; i <b.length; i ++) resultB.append (bytetohexString (b [i])); return resultB.tostring (); } private static string bytetoHexString (byte b) {int n = b; if (n <0) n += 256; int d1 = n / 16; int d2 = n % 16; return hexdigits [d1] + hexdigits [d2]; } public static string md5Encode (string asal, string charsetName) {string resultstring = null; Coba {hasilstring = string baru (asal); MessageDigest MD = MessageSmentIgest.getInstance ("md5"); if (charsetname == null || "" .Equals (charsetName)) hasilstring = bytearraytoHeHexString (md.digest (hasil hasil .getbytes ())); lain resultString = bytearraytoHexString (md.digest (resultString .getBytes (charsetName)))); } catch (pengecualian pengecualian) {} return resultString; } private static final string hexdigits [] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; } 2. Panggilan balik pembayaran
Setelah pembayaran selesai, WeChat akan mengirim hasil pembayaran yang relevan dan informasi pengguna ke alamat callback yang kami tentukan di atas. Kita perlu menerima pemrosesan dan mengembalikan balasan. Ketika berinteraksi dengan pemberitahuan latar belakang, jika WeChat menerima balasan pedagang tanpa keberhasilan atau waktu tunggu, dan WeChat percaya bahwa pemberitahuan tersebut telah gagal, WeChat akan secara teratur memulai pemberitahuan melalui strategi tertentu untuk meningkatkan tingkat keberhasilan pemberitahuan sebanyak mungkin, tetapi WeChat tidak menjamin bahwa pemberitahuan akan berhasil di akhir. (Frekuensi pemberitahuan adalah 15/15/30/1800/1800/1800/1800/1800/1800/3600, unit: detik)
Mengenai antarmuka panggilan balik pembayaran, kami harus terlebih dahulu menandatangani dan memverifikasi konten pemberitahuan hasil pembayaran, dan kemudian melakukan proses pemrosesan yang sesuai berdasarkan hasil pembayaran.
public void weixin_notify (permintaan httpservletRequest, respons httpservletResponse) melempar pengecualian {// baca parameter inputStream inputStream; StringBuffer SB = StringBuffer baru (); inputStream = request.getInputStream (); String s; BufferedReader di = BufferedReader baru (inputStreamReader baru (inputStream, "UTF-8"))); while ((s = in.readline ())! = null) {sb.append (s); } in.close (); inputStream.close (); // parse xml ke peta peta <string, string> m = new HashMap <string, string> (); m = xmlutil.doxmlparse (sb.tostring ()); // Filter Pengaturan Kosong TreeMap SortedMap <Object, Object> PackageParams = TREEMAP baru <Object, Object> (); Iterator it = m.keyset (). Iterator (); while (it.hasnext ()) {string parameter = (string) it.next (); String parameterValue = m.get (parameter); String v = ""; if (null! = parameterValue) {v = parameterValue.trim (); } packageParams.put (parameter, v); } // Informasi Akun String Key = payconfigutil.api_key; // kunci logger.info (packageParams); // Tentukan apakah tanda tangannya benar jika (paycommoneTil.ISTENPAYSIGN ("UTF-8", PackageParams, Key)) { // ------------------------------------------------------------------------------------------------------------------------------------------------------- Mulai // -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- (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); /////////// 执行自己的业务逻辑 ///////////////////info ("Pembayaran Sukses"); // Beri tahu WeChat. Konfirmasi asinkron berhasil. Harus menulisnya. Kalau tidak, latar belakang akan diberitahukan sepanjang waktu. Setelah delapan kali, akan dipertimbangkan bahwa transaksi telah gagal. resxml = "<xml>" + "<return_code> <! [cdata [sukses]]> </return_code>" + "<return_msg> <! [Cdata [ok]]> </eturn_msg>" + "</xml>"; } else {logger.info ("pembayaran gagal, pesan kesalahan:" + packageParams.get ("err_code"))); resxml = "<xml>" + "<return_code> <! [cdata [fail]]> </ return_code>" + "<return_msg> <! [CDATA [pesan kosong]]> </return_msg>" + "</xml>"; } // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Logger.info ("Verifikasi Tanda Tangan Pemberitahuan Gagal"); }} Algoritma verifikasi tanda tangan mirip dengan algoritma generasi tanda tangan, dan disediakan di kelas alat paycommonutil di atas.
3. Cerita selanjutnya
Saya merasa bahwa pengalaman pembayaran pemindaian WeChat cukup baik. Satu -satunya kelemahan adalah bahwa dokumen yang relevan tersebar. Demo resmi tidak ditulis di Jawa. Saya berharap pejabat pejabat WeChat secara bertahap dapat memperbaikinya di masa depan!
Di atas adalah semua konten artikel ini. Saya berharap ini akan membantu untuk pembelajaran semua orang dan saya harap semua orang akan lebih mendukung wulin.com.