1. التحضير
بادئ ذي بدء ، دعونا نشك في دفع WeChat. هناك العديد من نماذج الدفع التي تدعمها ، لكن المستندات الرسمية منتشرة بشكل خاص ، ولا توجد حتى عدد قليل من العروض التوضيحية اللائقة المرتبطة بـ Java. لم أقم بالدفع من قبل. لقد فاجأت حقًا في البداية. لقد حصلت عليه أخيرًا لمدة يومين. لقد كتبت هنا للاستمتاع بالأجيال القادمة!
فيما يتعلق بعمل التحضير ، فإن عنوان المستند الرسمي لـ "Wechat Scan QR Code Payment 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 2" (الدفع ورد الاتصال) ، سيتم استخدام App_ID و MCH_ID و API_Key فقط ، ولا يتم استخدام أي شيء آخر.
لن أتحدث عن بيئة التنمية. سواء كنت springmvc أو struts2 أو serverlet المباشر ، فهذا هو نفسه تقريبًا. طالما يمكنك التأكد من أنه يمكن استدعاء الطريقة المقابلة. فيما يتعلق بنقل حزم جرة الطرف الثالث ، استخدمت فقط JDOM الذي يعمل XML هنا. تذكر أنه هو الإصدار 1.* ، وليس الأحدث 2.* على الموقع الرسمي ، والاثنان غير متوافقين. على وجه التحديد ، هو JDOM-1.1.3.JAR ، حزمة التبعية Jaxen-1.1.6.Jar. بالنسبة لهاتين الحزمتين ، لم أستخدم HTTPClient المستخدمة في بعض الأمثلة. إنه شعور غير ضروري ، وحزمة التبعية معقدة للغاية. بالطبع ، أنت مافن عندما لم أقل ذلك.
2. التنمية والقتال العملي
1. أولاً ، اتصل بواجهة WeChat والحصول على رمز QR الدفع WeChat.
السلسلة العامة weixin_pay () يلقي استثناء {// معلومات الحساب appid = payConfigutil.app_id ؛ // appid // string appsecret = payConfigutil.app_secret ؛ // appsecret string mch_id = payConfigutil.mch_id ؛ // مفتاح سلسلة رقم الأعمال = payConfigutil.api_key ؛ // key string currtime = payCommonUtil.getCurrtime () ؛ String strime = currtime.substring (8 ، currtime.length ()) ؛ String strrandom = payCommonutil.BuildRandom (4) + "" ؛ سلسلة nonce_str = strime + strrrandom ؛ string order_price = 1 ؛ // السعر ملاحظة: يتم تقسيم وحدة السعر إلى body string = "goodsssssss" ؛ // اسم المنتج سلسلة out_trade_no = "11338" ؛ // رقم الطلب // الحصول على سلسلة IP الخاصة بالكمبيوتر spbill_create_ip = payConfigutil.create_ip ؛ // سلسلة واجهة رد الاتصال notify_url = payConfigutil.notify_url ؛ String Trade_type = "Native" ؛ sortedMap <object ، object> packageParams = new Treemap <object ، Object> () ؛ packparams.put ("appid" ، appid) ؛ packparams.put ("mch_id" ، mch_id) ؛ packparams.put ("nonce_str" ، nonce_str) ؛ packparams.put ("الجسم" ، الجسم) ؛ packparams.put ("out_trade_no" ، out_trade_no) ؛ packparams.put ("Total_fee" ، order_price) ؛ packparams.put ("spbill_create_ip" ، spbill_create_ip) ؛ packparams.put ("notify_url" ، notify_url) ؛ packparams.put ("trade_type" ، trade_type) ؛ STRING SIGN = PAYCOMMONUTIL.CREATESIGN ("UTF-8" ، PackageParams ، Key) ؛ packparams.put ("علامة" ، علامة) ؛ 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") ؛ سلسلة urlcode = (string) map.get ("code_url") ؛ إرجاع urlcode. }في حالة عدم وجود شيء غير متوقع ، نحصل على عنوان URL للدفع من خادم WeChat ، والذي يبدو وكأنه Weixin: // WXPay/BizPayurl؟ pr = pixxxxx. بعد ذلك ، نحتاج إلى إنشاء رمز الاستجابة السريعة لعنوان URL هذا ، وبعد ذلك يمكننا استخدام هاتفنا المحمول WeChat لمسح الرمز لدفعه. هناك العديد من الطرق لتوليد رموز QR. يرجى أخذ احتياجاتك الخاصة. أقوم بتقديم واجهة توليد رمز QR هنا:
السلسلة الثابتة العامة QRFromGOogle (سلسلة chl) يلقي استثناء {int widhtheight = 300 ؛ String ec_level = "l" ؛ الهامش int = 0 ؛ Chl = urlencode (CHL) ؛ String qrfromgoogle = "http://chart.apis.google.com/chart؟chs=" + widhtheight + "x" + widhtheight + "& cht = qr & chld =" + ec_level + "|" + margin + "& chl =" + chl ؛ إرجاع qrfromgoogle ؛ }. }
يتضمن الرمز أعلاه عدة فئات الأدوات: PayConfigutil و PayCommonutil و Httputil و Xmlutil. من بينها ، يضع PayConfigutil بعض التكوينات والمسارات المذكورة أعلاه. يتضمن PayCommonutil عدة طرق للحصول على الحدث الحالي ، وإنشاء سلاسل عشوائية ، والحصول على توقيعات المعلمة وربط XML. الرمز كما يلي:
paycommonutil من الفئة العامة { /*** ما إذا كان التوقيع صحيحًا ، فإن القاعدة هي: الفرز حسب اسم المعلمة AZ ، والمعلمات التي تواجه قيمًا فارغة لا تشارك في التوقيع. * return boolean */ public static boolean istenpaysign (string characterencoding ، sortedMap <object ، object> packageparams ، string api_key) {StringBuffer sb = new StringBuffer () ؛ set es = packageparams.entryset () ؛ iterator it = es.iterator () ؛ بينما (it.hasnext ()) {map.entry entry = (map.entry) it.next () ؛ سلسلة k = (سلسلة) entry.getKey () ؛ String V = (String) Entpl.getValue () ؛ if (! "sign" .equals (k) && null! = v &&! }} sb.append ("key =" + api_key) ؛ // ملف سلسلة ملخص mysign = md5util.md5encode (sb.toString () ، characterencoding) .toLowerCase () ؛ String TenPaySign = ((string) packageParams.get ("sign")). tolowercase () ؛ //system.out.println(EntPaySign + "" + mySign) ؛ إرجاع tenpaysign.equals (mySign) ؛ } / ** * author * date 2016-4-22 * description: تسجيل توقيع * param charactering * تنسيق الترميز * param parameters * request request * @return * / public static string createSign (string characterencoding ، sortedMap <Object> packparams ، string api_key) { set es = packageparams.entryset () ؛ iterator it = es.iterator () ؛ بينما (it.hasnext ()) {map.entry entry = (map.entry) it.next () ؛ سلسلة k = (سلسلة) entry.getKey () ؛ String V = (String) Entpl.getValue () ؛ if (null! = v &&! "". equals (v) &&! "sign" .equals (k) &&! }} sb.append ("key =" + api_key) ؛ STRING SIGN = MD5UTIL.MD5ENCODE (SB.TOSTRING () ، characterencoding) .ToupperCase () ؛ علامة العودة } / ** * Author * date 2016-4-22 * description: تحويل معلمات الطلب إلى سلسلة تنسيق XML * param parameters * request parameters * / static string static getRequestXML (sortedMap <object ، objects) sb.append ("<xml>") ؛ set es = parameters.entryset () ؛ iterator it = es.iterator () ؛ بينما (it.hasnext ()) {map.entry entry = (map.entry) it.next () ؛ سلسلة k = (سلسلة) entry.getKey () ؛ String V = (String) Entpl.getValue () ؛ إذا ("إرفاق" .equalsignorecase (k) || } آخر {sb.append ("<" + k + ">" + v + "</" + k + ">") ؛ }}} sb.append ("</xml>") ؛ إرجاع sb.tostring () ؛ } /*** قم بإخراج عدد صحيح إيجابي عشوائي لحجم الطول المحدد. * * param length * int قم بتعيين طول الرقم العشوائي المسترد. طول أقل من 11 * @RETURN int إرجاع الرقم العشوائي الذي تم إنشاؤه. */ public static int buildRandom (int length) {int num = 1 ؛ عشوائي مزدوج = Math.Random () ؛ if (عشوائي <0.1) {عشوائي = عشوائي + 0.1 ؛ } لـ (int i = 0 ؛ i <length ؛ i ++) {num = num * 10 ؛ } return (int) ((عشوائي * num)) ؛ } / ** * احصل على الوقت الحالي yyyymmddhhmmss * * @regurn string * / public static string getCurtime () {date now = new date () ؛ SimpleTformat outFormat = new SimpledAteFormat ("YyyyMmdDhhmmss") ؛ سلسلة s = outformat.format (الآن) ؛ العودة s ؛ }}Httputil و Xmlutil كما يلي:
الفئة العامة httputil {private static final logger = logs.get () ؛ int static static int int_timeout = 5000 ؛ // في ملايين ثانية من سلسلة static static default_encoding = "utf-8" ؛ السلسلة الثابتة العامة postdata (سلسلة urlstr ، بيانات السلسلة) {return postData (urlstr ، data ، null) ؛ } السلسلة الثابتة العامة postdata (سلسلة urlstr ، بيانات السلسلة ، contentType) {bufferedReader reader = null ؛ حاول {url url = url new (urlstr) ؛ urlConnection conn = url.openconnection () ؛ conn.setDooutput (صواب) ؛ conn.setConnectTimeOut (connect_timeout) ؛ conn.setReadTimeout (connect_timeout) ؛ if (contentType! = null) conn.setRequestProperty ("نوع المحتوى" ، contentType) ؛ OutputStreamWriter Writer = new OutputStreamWriter (conn.getOutputStream () ، default_encoding) ؛ if (data == null) data = "" ؛ Writer.write (Data) ؛ الكاتب. flush () ؛ الكاتب. reader = جديد bufferedReader (new inputStreamReader (conn.getInputStream () ، default_encoding)) ؛ StringBuilder sb = new StringBuilder () ؛ خط السلسلة = فارغ ؛ بينما ((line = reader.ReadLine ())! = null) {sb.append (line) ؛ sb.append ("/r/n") ؛ } return sb.toString () ؛ } catch (ioException e) {logger.error ("خطأ الاتصال بـ" + urlstr + ":" + e.getMessage ()) ؛ } أخيرًا {try {if (reader! = null) reader.close () ؛ } catch (ioException e) {}} return null ؛ }} الفئة العامة Xmlutil { /*** تحليل XML وإرجاع زوج مفتاح العنصر من المستوى الأول. إذا كان العنصر من المستوى الأول يحتوي على أطفال ، فإن قيمة هذه العقدة هي بيانات XML للعقدة الفرعية. * param strxml * return * throws jdomexception * throws ioException */map static public doxmlparse (String strxml) rems jdomexception ، ioException {strxml = strxml.replacefirst ("encoding =/". if (null == strxml || "" .equals (strxml)) {return null ؛ } map m = new hashmap () ؛ inputStream in = new ByTearRayInputStream (strxml.getBytes ("UTF-8")) ؛ SaxBuilder Builder = new Saxbuilder () ؛ مستند المستند = builder.build (in) ؛ العنصر جذر = doc.getRootElement () ؛ قائمة قائمة = root.getChildRen () ؛ iterator it = list.iterator () ؛ بينما (it.hasnext ()) {element e = (element) it.next () ؛ سلسلة k = e.getName () ؛ سلسلة V = "" ؛ قائمة الأطفال = E.GetChildren () ؛ if (children.isempty ()) {v = e.getTextNormalial () ؛ } آخر {v = xmlutil.getChildRentext (الأطفال) ؛ } m.put (k ، v) ؛ } // أغلق الدفق in.close () ؛ العودة م ؛ } / ** * احصل على XML من Node Child * param children * @regurn string * / public static string getChildRentext (قائمة الأطفال) {StringBuffer sb = new StringBuffer () ؛ if (! children.isempty ()) {iterator it = children.iterator () ؛ بينما (it.hasnext ()) {element e = (element) it.next () ؛ اسم السلسلة = e.getName () ؛ قيمة السلسلة = e.getTextNormalial () ؛ قائمة قائمة = e.getChildren () ؛ sb.append ("<" + name + ">") ؛ if (! list.isempty ()) {sb.append (xmlutil.getChildRentext (list)) ؛ } sb.append (value) ؛ sb.append ("</" + name + ">") ؛ }} return sb.toString () ؛ }} بالطبع هناك أيضًا فئة أدوات الحوسبة MD5
الفئة العامة MD5UTIL {سلسلة ثابتة خاصة 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 ؛ إذا (n <0) n += 256 ؛ int d1 = n / 16 ؛ int d2 = n ٪ 16 ؛ إرجاع hexdigits [d1] + hexdigits [d2] ؛ } السلسلة الثابتة العامة md5encode (أصل سلسلة ، سلسلة charsetName) {String ResultString = null ؛ جرب {resultString = New String (Origin) ؛ messagedigest md = messagedigest.getInstance ("MD5") ؛ إذا (charsetName == null || ". نتائج أخرى = bytearraytoexstring (md.digest (resulttring .getBytes (charsetName))) ؛ } catch (استثناء استثناء) {} return resulttring ؛ } hexdigits hexdigits hexdigits الثابتة الثابتة [] = {"0" ، "1" ، "2" ، "3" ، "4" ، "5" ، "6" ، "7" ، "8" ، "9" ، "A" ، "B" ، "C" ، "D" ، "E" ، "F" ؛ } 2. رد الاتصال على الدفع
بعد اكتمال الدفع ، سترسل WeChat نتائج الدفع ذات الصلة ومعلومات المستخدم إلى عنوان رد الاتصال الذي حددناه أعلاه. نحتاج إلى تلقي المعالجة وإعادة الرد. عند التفاعل مع إخطارات الخلفية ، إذا تلقى WeChat رد التاجر دون نجاح أو مهلة ، ويعتقد WeChat أن الإخطار قد فشل ، فإن WeChat سيعيد تشغيل الإخطارات بانتظام من خلال استراتيجيات معينة لزيادة معدل النجاح في الإخطارات قدر الإمكان ، لكن WeChat لا يضمن أن الإخطار سوف ينجح في النهاية. (تردد الإخطار هو 15/15/30/1800/1800/1800/1800/1800/1800/3600 ، الوحدة: ثواني)
فيما يتعلق بواجهة رد الاتصال على الدفع ، يجب علينا أولاً التوقيع والتحقق من محتوى إشعار نتيجة الدفع ، ثم إجراء عملية المعالجة المقابلة بناءً على نتيجة الدفع.
Public void weixin_notify (طلب httpservletrequest ، استجابة httpservletresponse) يلقي الاستثناء {// قراءة المعلمات inputStream ؛ StringBuffer SB = New StringBuffer () ؛ inputStream = request.getInputStream () ؛ سلسلة S ؛ BufferedReader في = جديد BufferEdReader (New InputStreamReader (inputStream ، "UTF-8")) ؛ بينما ((s = in.readline ())! = null) {sb.append (s) ؛ } in.close () ؛ inputStream.Close () ؛ // parse xml في خريطة الخريطة <string ، string> m = new HashMap <string ، string> () ؛ m = xmlutil.doxmlparse (sb.toString ()) ؛ // تصفية الإعدادات الفارغة treemap sortedMap <object ، object> packageParams = new Treemap <object ، Object> () ؛ iterator it = m.keyset (). iterator () ؛ بينما (it.hasnext ()) {string parameter = (string) it.next () ؛ سلسلة parametervalue = m.get (المعلمة) ؛ سلسلة V = "" ؛ if (null! = parametervalue) {v = parametervalue.trim () ؛ } packparams.put (المعلمة ، v) ؛ } // مفتاح سلسلة معلومات الحساب = payConfigutil.api_key ؛ // key logger.info (packageParams) ؛ // حدد ما إذا كان التوقيع صحيحًا إذا (paycommonutil.istenpaysign ("utf-8" ، packparams ، key)) { // ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- تبدأ // ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- (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) ؛ ////////// 执行自己的业务逻辑 ///////////////////////////////// // إخطار WeChat. التأكيد غير المتزامن ناجح. يجب كتابتها. خلاف ذلك ، سيتم إخطار الخلفية طوال الوقت. بعد ثماني مرات ، سيتم اعتبار أن المعاملة قد فشلت. resxml = "<xml>" + "<Return_Code> <! } آخر {logger.info ("فشل الدفع ، رسالة خطأ:" + packageParams.get ("err_code")) ؛ resxml = "<xml>" + "<Return_Code> <! }. logger.info ("فشل التحقق من توقيع الإخطار") ؛ }} تشبه خوارزمية التحقق من التوقيع خوارزمية توليد التوقيع ، ويتم توفيرها في فئة أدوات PayCommonutil أعلاه.
3. قصص لاحقة
أشعر أن تجربة دفع WeChat Scanning جيدة جدًا. العيب الوحيد هو أن المستندات ذات الصلة منتشرة. العرض التجريبي الرسمي غير مكتوب في جافا. آمل أن يتمكن مسؤول WeChat الرسمي من تحسينه تدريجياً في المستقبل!
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.