1。準備
まず第一に、WeChatの支払いについて不平を言ってみましょう。サポートするいくつかの支払いモデルがありますが、公式文書は特に散在しており、Java関連のデモもいくつかありません。私は以前にWeChatの支払いをしたことがありません。私は最初に本当にst然としました。私はついに2日間それを手に入れました。将来の世代を楽しむためにここに書き留めました!
準備作業に関しては、「WeChat Scan QR Code Payment Model 2」の公式ドキュメントアドレスはhttps://pay.weixin.qq.com/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、またはDirect Serverletであろうと、ほぼ同じです。対応するメソッドを呼び出すことができる限り。サードパーティのジャーパッケージの引用に関して、ここでXMLを操作するJDOMのみを使用しました。 1.*バージョンではなく、最新の2.*であり、公式Webサイトでは2つのバージョンであり、2つは互換性がありません。具体的には、JDOM-1.1.3.Jar、依存関係パッケージJaxen-1.1.6.jarです。これら2つのパッケージでは、いくつかの例で使用されているHTTPClientを使用しませんでした。それは不要だと感じており、依存関係パッケージは非常に複雑です。もちろん、私がそれを言わなかったとき、あなたは狂っています。
2。開発と実用的な戦闘
1.最初に、WeChatインターフェイスに接続し、WeChat Payment QRコードを取得します。
public string weixin_pay()スロー例外{//アカウント情報文字列appid = payconfigutil.app_id; // appid // string appsecret = payconfigutil.app_secret; // appsecret string mch_id = payconfigutil.mch_id; //ビジネス番号文字列key = payconfigutil.api_key; //キー文字列currime = paycommonutil.getCurrime(); string strtime = currime.substring(8、currime.length()); string strrandom = paycommonutil.buildrandom(4) + "";文字列nonce_str = strtime + strandom; string order_price = 1; //価格注:価格の単位は弦body = "goodsssssss"に分割されます。 //製品名String out_trade_no = "11338"; //注文番号//開始コンピューターIP文字列spbill_create_ip = payconfigutil.create_ip;を取得します。 // callback interface string 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( "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 = 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を返します。 }予期しないことが起こっていない場合、WeChatサーバーから支払いURLを取得します。これは、Weixin:// wxpay/bizpayurl?pr = pixxxxxのように見えます。その後、このURLのQRコードを生成する必要があります。その後、携帯電話のWeChatを使用してコードをスキャンして支払うことができます。 QRコードを生成するには多くの方法があります。あなた自身のニーズをとってください。ここでGoogle QRコード生成インターフェイスを提供しています:
public static string qrfromgoogle(string chl)スロー例外{int widhtheight = 300;文字列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 + "|" +マージン + "&chl =" + chl; qrfromgoogleを返します。 } //特殊文字処理public static string urlencode(string src)sulows unsupportedencodingectection {return urlencoder.encode(src、 "utf-8")。 }上記のコードには、PayConfigutil、PayCommonutil、Httputil、Xmlutilのいくつかのツールクラスが含まれます。その中で、PayConfigutilは上記のいくつかの構成とパスを置きます。 PayCommonutilには、現在のイベントを取得し、ランダムな文字列を生成し、パラメーター署名を取得し、XMLをスプライシングするいくつかの方法が含まれます。コードは次のとおりです。
Public Class PayCommonutil { /***署名が正しいかどうか、ルールは次のとおりです。パラメーター名AZでソートし、空の値に遭遇するパラメーターは署名に参加しません。 * @return boolean */ public static boolean istenpaysign(string charatereCoding、sortedmap <object、object> packageparams、string api_key){stringbuffer sb = new StringBuffer(); set es = packageParams.EntrySet(); iterator it = es.iterator(); while(it.hasnext()){map.entry entry =(map.entry)it.next();文字列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); // summary文字列mysign = md5util.md5encode(sb.tostring()、charatereCoding).tolowercase(); string tenpaysign =((string)packageparams.get( "sign"))。tolowercase(); //system.out.println(tenpaysign + "" + mysign); return tenpaysign.equals(mysign); } / ** * @author * @date 2016-4-22 * @description:署名署名 * @param CharacterEncoding * encoding format * @paramパラメーター * @return * / public static string createsign(string charaterencoding、sortedmap <object、object> object> packageparams、string api_key = string buffer sber() set es = packageParams.EntrySet(); iterator it = es.iterator(); while(it.hasnext()){map.entry entry =(map.entry)it.next();文字列k =(string)entry.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()、charatereCoding).touppercase();戻りサイン。 } / ** * @author * @date 2016-4-22 * @description:要求パラメーターをXMLフォーマット文字列に変換 * @paramパラメーター * @return * / public static string getRequestxml(sortedmap <object、object> parameters){Stringbuffer sb = new StringBuffer(); sb.append( "<xml>"); set es = parameters.entryset(); iterator it = es.iterator(); while(it.hasnext()){map.entry entry =(map.entry)it.next();文字列k =(string)entry.getKey(); string v =(string)entry.getValue(); if( "Attach" .equalsignorecase(k)|| "body" .equalsignorecase(k)|| "sign" .equalsignorecase(k)){sb.append( "<" + k + ">" + "<![cdata [" + v + "]> </" + " +"> } else {sb.append( "<" + k + ">" + v + "</" + k + ">"); }}} sb.append( "</xml>"); return sb.tostring(); } /***指定された長さサイズのランダムな正の整数を取り出します。 * * @param Length * int取得した乱数の長さを設定します。長さ11 * @return int生成された乱数を返します。 */ public static int buildrandom(int length){int num = 1; double random = math.random(); if(random <0.1){random = random + 0.1; } for(int i = 0; i <length; i ++){num = num * 10; } return(int)((random * num)); } / ** *現在の時間を取得yyyymmddhhmmss * * @return string * / public static string getCurrime(){date now = new date(); simpledateFormat offormat = new simpledateFormat( "yyyymmddhhmmss"); string s = offormat.format(now); s; }}httputilとxmlutilは次のとおりです。
public class httputil {private static final logger = logs.get();プライベート最終static 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();文字列line = null; while((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; }} public class xmlutil { /*** xmlを解析し、第1レベルの要素キー値ペアを返します。第1レベルの要素に子供がいる場合、このノードの値は子ノードのXMLデータです。 * @param strxml * @return * @throws jdomexception * @throws ioexception */public static Map doxmlparse(string strxml)throws 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(); document doc = builder.build(in);要素root = doc.getRootelement();リストリスト= root.getChildren(); iterator it = list.iterator(); while(it.hasnext()){element e =(element)it.next();文字列k = e.getName();文字列v = "";子供のリスト= e.getChildren(); if(childer.isempty()){v = e.getTextNormalize(); } else {v = xmlutil.getChildRentext(children); } m.put(k、v); } // in.close(); mを返します。 } / ** *子ノードのXMLを取得 * @Param Children * @return String * / public Static String getChildRentext(list Children){stringbuffer sb = new StringBuffer(); if(!children.isempty()){iterator it = childer.iterator(); while(it.hasnext()){element e =(element)it.next();文字列名= e.getName();文字列値= e.getTextNormalize();リストリスト= e.getChildren(); sb.append( "<" + name + ">"); if(!list.isempty()){sb.append(xmlutil.getChildRentext(list)); } sb.append(value); sb.append( "</" + name + ">"); }} return sb.toString(); }}もちろん、MD5コンピューティングツールクラスもあります
public class md5util {private static string bytearraytohexstring(byte b []){stringbuffer resultsb = new StringBuffer(); for(int i = 0; i <b.length; i ++)resultsb.append(bytetohexstring(b [i])); return resultsb.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 origin、string charsetname){string resultstring = null; try {resultString = new String(Origin); MESSAGEDGEST MD = MESSAGEDGEST.GETINSTANCE( "MD5"); if(charsetname == null || "" .equals(charsetname))resultString = bytearraytohexstring(md.digest(resterstring .getbytes())); else resultstring = bytearraytohexstring(md.digest(resultString .getBytes(charsetname))); } catch(例外例外){} return resultString; } private static final string hexdigits [] = {"0"、 "1"、 "2"、 "3"、 "4"、 "5" 6 "、" 7 "、" 8 "、9"、 "a"、 "b"、 " } 2。支払いコールバック
支払いが完了した後、WeChatは関連する支払い結果とユーザー情報を上記で指定したコールバックアドレスに送信します。処理を受け取り、返信を返す必要があります。バックグラウンド通知と対話する場合、WeChatが成功やタイムアウトなしで商人の返信を受信し、WeChatが通知が失敗したと考えている場合、WeChatは特定の戦略を通じて通知を定期的に再度開始して、通知の成功率を可能な限り上げますが、WeChatは通知が終了することを保証しません。 (通知頻度は15/15/30/1800/1800/1800/1800/1800/1800/3600、ユニット:秒)
支払いコールバックインターフェイスについては、最初に支払い結果通知のコンテンツに署名して検証し、次に支払い結果に基づいて対応する処理プロセスを実行する必要があります。
public void weixin_notify(httpservletrequest request、httpservletresponse応答)をスローする{//パラメータ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(); // XMLをマップマップに分割<文字列、文字列> m = new Hashmap <String、String>(); m = xmlutil.doxmlparse(sb.tostring()); //空の設定をフィルターTreemap sortedmap <object、object> packageparams = new Treemap <object、object>(); iterator it = m.keyset()。iterator(); while(it.hasnext()){string parameter =(string)it.next(); string parametervalue = m.get(parameter);文字列v = ""; if(null!= parametervalue){v = parametervalue.trim(); } packageParams.put(parameter、v); } //アカウント情報文字列key = payconfigutil.api_key; // key logger.info(packageparams); //署名が正しいかどうかを判断します //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- //Processing business 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); ///////执行自己的业务逻辑//////////////agger.info( "支払い成功"); // wechatに通知します。非同期確認は成功しました。書く必要があります。それ以外の場合、背景に常に通知されます。 8回後、トランザクションが失敗したと見なされます。 resxml = "<xml>" + "<return_code> <![cdata [success]]> </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をもっとサポートすることを願っています。