Android—基於微信開放平臺v3SDK,開發微信支付填坑。

来源:http://www.cnblogs.com/yunfang/archive/2016/11/18/6078208.html
-Advertisement-
Play Games

接觸微信支付之前聽說過這是一個坑,,,心裡已經有了準備。。。我以為我沒準跳坑出不來了,沒有想到我填上了,調用成功之後我感覺公司所有的同事都是漂亮的,隔著北京的大霧霾我仿佛看見了太陽~~~好了,裝逼結束。。。進入正題 開發準備: 1.在微信開放平臺申請賬號 2.成功後創建應用,就是填一些看似很官方很正 ...


接觸微信支付之前聽說過這是一個坑,,,心裡已經有了準備。。。我以為我沒準跳坑出不來了,沒有想到我填上了,調用成功之後我感覺公司所有的同事都是漂亮的,隔著北京的大霧霾我仿佛看見了太陽~~~好了,裝逼結束。。。進入正題

開發準備:

1.在微信開放平臺申請賬號

2.成功後創建應用,就是填一些看似很官方很正經的資料了。。。(說審核7天左右,沒有意外的情況下你的app第二天就審核成功了是不是很開心,有了appid,是不是就可以調用微   信支付了????-------想多了,真的)

3.微信支付是需要額外申請的:需要資料審核,賬戶驗證,協議簽署等步驟,(我記得,,資料審核要填寫的東西好多,,,好多,,,賬戶驗證就是你審核成功後微信會發送郵件到你   註冊時登記的郵箱賬號,其中含有隨機金額用於賬戶驗證,協議簽署,略,太簡單)一定要好好閱讀你郵件的任何信息,因為有的細節錯了,,,你可能填坑很久。。。。。。

正式開發階段:

問題1:

調用官方的SDK發現只能成功的調起一次微信,再次支付的時候怎麼也調用不起來了

解決:

似乎不是什麼正經方法:在手機設置中管理應用程式,清除微信數據,緩存,,再來一遍,絕對可以調起來(當然還是只是一次。。。。)

繼續:

我認為要用微信支付嘛,,就只看了調用支付介面的文檔:

後來發現需要的參數prepayid是怎麼也找不到啊,,後來才發現這個prepayid是先調用”統一下單“這個介面時候溫馨反過來的東西,但是官方的SDK中並沒有統一下單的代碼。坑嗎???

所以要先看統一下單文檔了 

1.使用自己的APP調用的時候把官網down下來的SDK中WXPayEntryActivity拷貝到自己的項目,所在包的名字最後一定是.wxapi(我連包一起拷了。。。。)

2.在項目清單文件中填寫:

 

 3.SDK中的AppRegister拷貝下來,,,裡面換成你自己的appid,然後在項目清單中也註冊一下。

 

 

4.重點來了,,就是你APP要調微信支付的activity,我這還叫PayActivity

 要調起微信支付頁面,要在這個activity中,將你的app註冊到微信

 

接下來先調用統一下單獲取prepayid,參數,微信人家要xml格式的!我們就得發送xml格式的!

 

好了調用的時候把這個方法的返回值當參數傳,,等待微信返回success吧!。。

 我遇見的錯誤:簽名錯誤

我的原因是大意了 ConfigUtil.NOTIFY_URL這個參數寫的空字元串

還有是因為debug版運行的,沒有打包運行

返回成功之後,可以調用支付介面了

 

不要忘了這一步去跳轉界面,,,,,

沒有跳轉到微信支付可能是你由運行的debug版本,,,,沒有打包。。。。。

下麵是我的PayActivity完整代碼:

package com.example.taijiapp.ui;

import java.io.StringReader;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.xmlpull.v1.XmlPullParser;

import com.example.taijiapp.R;
import com.example.taijiapp.util.Constants;
import com.example.taijiapp.util.MD5;
import com.example.taijiapp.util.T;
import com.example.taijiapp.util.Util;
import com.example.taijiapp.utils.ConfigUtil;
import com.tencent.mm.sdk.modelpay.PayReq;
import com.tencent.mm.sdk.openapi.IWXAPI;
import com.tencent.mm.sdk.openapi.WXAPIFactory;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.util.Xml;
import android.view.View;
import android.widget.Button;

public class PayActivity extends Activity {
	PayReq req;
	private IWXAPI msgApi;
	Map<String, String> resultunifiedorder;
	StringBuffer sb;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.pay);
		req = new PayReq();
		sb = new StringBuffer();
		msgApi = WXAPIFactory.createWXAPI(this, Constants.APP_ID);
		/**
		 * 將app註冊到微信
		 */
		boolean registerApp = msgApi.registerApp(Constants.APP_ID);
		T.show(this, "註冊========"+registerApp+"");
		Button appayBtn = (Button) findViewById(R.id.appay_btn);
		Button check_pay_btn = (Button) findViewById(R.id.check_pay_btn);
		appayBtn.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				GetPrepayIdTask getPrepayId = new GetPrepayIdTask();
				getPrepayId.execute();
			}
		});
		/**
		 * 將該app註冊到微信
		 */
		check_pay_btn.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				genPayReq();
				sendPayReq();
				
			}
		});
//		// 生成簽名參數
//		Button appay_pre_btn = (Button) findViewById(R.id.appay_pre_btn);
//		appay_pre_btn.setOnClickListener(new View.OnClickListener() {
//
//			@Override
//			public void onClick(View v) {
//				genPayReq();
//			}
//		});
	}
	/**
	 * 生成簽名
	 */

	private String genPackageSign(List<NameValuePair> params) {
		StringBuilder sb = new StringBuilder();

		for (int i = 0; i < params.size(); i++) {
			sb.append(params.get(i).getName());
			sb.append('=');
			sb.append(params.get(i).getValue());
			sb.append('&');
		}
		sb.append("key=");
		sb.append(Constants.KEY);
		Log.e("拼接=====", sb.toString());
		String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
		Log.e("orion生成簽名===", packageSign);
		return packageSign;
	}

	/**
	 * 簽名工具 不含商戶密鑰 -暫時不用 = * 編碼格式 UTF-8 = * @return
	 */
	public static String createSignNoKey(List<NameValuePair> params) {
		StringBuilder sb = new StringBuilder();

		for (int i = 0; i < params.size(); i++) {
			sb.append(params.get(i).getName());
			sb.append('=');
			sb.append(params.get(i).getValue());
			sb.append('&');
		}
		String signStr = sb.toString();
		String subStr = signStr.substring(0, signStr.length() - 1);
		// 註意sign轉為大寫
		return MD5.getMessageDigest(subStr.getBytes()).toUpperCase();
	}

	private String genAppSign(List<NameValuePair> params) {
		StringBuilder sb = new StringBuilder();

		for (int i = 0; i < params.size(); i++) {
			sb.append(params.get(i).getName());
			sb.append('=');
			sb.append(params.get(i).getValue());
			sb.append('&');
		}
		sb.append("key=");
		sb.append(Constants.KEY);

		this.sb.append("sign str\n" + sb.toString() + "\n\n");
		String appSign = MD5.getMessageDigest(sb.toString().getBytes());
		Log.e("orion", appSign);
		return appSign;
	}

	private String toXml(List<NameValuePair> params) {
		StringBuilder sb = new StringBuilder();
		sb.append("<xml>");
		for (int i = 0; i < params.size(); i++) {
			sb.append("<" + params.get(i).getName() + ">");

			sb.append(params.get(i).getValue());
			sb.append("</" + params.get(i).getName() + ">");
		}
		sb.append("</xml>");

		Log.e("orion", sb.toString());
		return sb.toString();
	}

	private class GetPrepayIdTask extends AsyncTask<Void, Void, Map<String, String>> {

		private ProgressDialog dialog;

		@Override
		protected void onPreExecute() {
			dialog = ProgressDialog.show(PayActivity.this, getString(R.string.app_tip),
					getString(R.string.getting_prepayid));
		}

		@Override
		protected void onPostExecute(Map<String, String> result) {
			if (dialog != null) {
				dialog.dismiss();
			}
			sb.append("prepay_id\n" + result.get("prepay_id") + "\n\n");

			resultunifiedorder = result;

		}

		@Override
		protected void onCancelled() {
			super.onCancelled();
		}

		@Override
		protected Map<String, String> doInBackground(Void... params) {

			String url = String.format("https://api.mch.weixin.qq.com/pay/unifiedorder");
			String entity = genProductArgs();
			Log.e("orion===發送過去", entity);
			byte[] buf = Util.httpPost(url, entity);

			String content = new String(buf);
			Log.e("orion", content);
			Map<String, String> xml = decodeXml(content);

			return xml;
		}
	}

	public Map<String, String> decodeXml(String content) {

		try {
			Map<String, String> xml = new HashMap<String, String>();
			XmlPullParser parser = Xml.newPullParser();
			parser.setInput(new StringReader(content));
			int event = parser.getEventType();
			while (event != XmlPullParser.END_DOCUMENT) {

				String nodeName = parser.getName();
				switch (event) {
				case XmlPullParser.START_DOCUMENT:

					break;
				case XmlPullParser.START_TAG:

					if ("xml".equals(nodeName) == false) {
						// 實例化student對象
						xml.put(nodeName, parser.nextText());
					}
					break;
				case XmlPullParser.END_TAG:
					break;
				}
				event = parser.next();
			}

			return xml;
		} catch (Exception e) {
			Log.e("orion", e.toString());
		}
		return null;

	}
    /**
     * 生成隨機數
     * @return
     */
	private String genNonceStr() {
		Random random = new Random();
		return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
	}
    /**
     * 時間戳
     * @return
     */
	private long genTimeStamp() {
		return System.currentTimeMillis() / 1000;
	}
    /**
     * 隨機數
     * @return
     */
	private String genOutTradNo() {
		Random random = new Random();
		return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
	}
    /**
     * 獲取設備的ip地址
     * @return
     */
	public String getLocalIpAddress() {
		try {
			for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
				NetworkInterface intf = en.nextElement();
				for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
					InetAddress inetAddress = enumIpAddr.nextElement();
					if (!inetAddress.isLoopbackAddress()) {
						return inetAddress.getHostAddress().toString();
					}
				}
			}
		} catch (SocketException ex) {
		}
		return null;
	}

	private String getWifiIp() {
		// 獲取wifi服務
		WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
		// 判斷wifi是否開啟
		if (!wifiManager.isWifiEnabled()) {
			wifiManager.setWifiEnabled(true);
		}
		WifiInfo wifiInfo = wifiManager.getConnectionInfo();
		int ipAddress = wifiInfo.getIpAddress();
		String ip = intToIp(ipAddress);
		return ip;
	}
    /**
     * 轉化成Ip地址的格式
     * @param i
     * @return
     */
	private String intToIp(int i) {

		return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." + (i >> 24 & 0xFF);
	}

	private String genProductArgs() {
		StringBuffer xml = new StringBuffer();
		String ip = getWifiIp();
		if (ip == "" && ip == "") {
			ip = getLocalIpAddress();
		}
		try {
			String nonceStr = genNonceStr();
			xml.append("</xml>");
			List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
			packageParams.add(new BasicNameValuePair("appid", Constants.APP_ID));
			packageParams.add(new BasicNameValuePair("body", "APPtest"));
			packageParams.add(new BasicNameValuePair("mch_id", Constants.MCH_ID));
			packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
			packageParams.add(new BasicNameValuePair("notify_url", ConfigUtil.NOTIFY_URL));
			packageParams.add(new BasicNameValuePair("out_trade_no", genOutTradNo()));
			packageParams.add(new BasicNameValuePair("spbill_create_ip", ip));
			packageParams.add(new BasicNameValuePair("total_fee", "100"));
			packageParams.add(new BasicNameValuePair("trade_type", "APP"));
			String sign = genPackageSign(packageParams);
			packageParams.add(new BasicNameValuePair("sign", sign));
			String xmlstring = toXml(packageParams);
			return xmlstring;
		} catch (Exception e) {
			Log.e("TAG", "fail, ex = " + e.getMessage());
			return null;
		}
	}

	private void genPayReq() {
		req.appId = Constants.APP_ID;
		req.partnerId = Constants.MCH_ID;
		req.prepayId = resultunifiedorder.get("prepay_id");
		req.packageValue = "prepay_id=" + resultunifiedorder.get("prepay_id");
		req.nonceStr = genNonceStr();
		req.timeStamp = String.valueOf(genTimeStamp());
		List<NameValuePair> signParams = new LinkedList<NameValuePair>();
		signParams.add(new BasicNameValuePair("appid", req.appId));
		signParams.add(new BasicNameValuePair("noncestr", req.nonceStr));
		signParams.add(new BasicNameValuePair("package", req.packageValue));
		signParams.add(new BasicNameValuePair("partnerid", req.partnerId));
		signParams.add(new BasicNameValuePair("prepayid", req.prepayId));
		signParams.add(new BasicNameValuePair("timestamp", req.timeStamp));
		req.sign = genAppSign(signParams);
		sb.append("sign\n" + req.sign + "\n\n");
		Log.e("orion==genPayReq===============", signParams.toString());

	}

	private void sendPayReq() {
		boolean sendReq = msgApi.sendReq(req);
		T.show(this, "微信跳轉====="+sendReq+"");
	}

}

裡面沒有的類,,,,吶: http://download.csdn.net/detail/meijuanyou/9571223  自己下載一下拷貝一下。。。

參考博客      https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1

 備忘,希望能給大家帶來幫助。。。

 

 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 構造函數、原型鏈: ...
  • 在我們使用php導入和導出excel表格的時候經常會見到 enctype="multipart/form-data",哪他的作用是什麼呢? ENCTYPE="multipart/form-data"用於表單里有圖片上傳。 <form name="userInfo" method="post" act ...
  • 最近用到了Dynatree的樹形結構,記錄一下它的用法。 需求: 1.jquery.js 2.jquery-ui.custom.js 3.jquery.cookie.js 下載dynatree,放到資源路徑下,在頁面引入ui.dynatree.css,jquery.dynatree.js。 以上是基 ...
  • 作用: 1 隱藏域在頁面中對於用戶是不可見的,在表單中插入隱藏域的目的在於收集或發送信息,以利於被處理表單的程式所使用。瀏覽者單擊發送按鈕發送表單的時候,隱藏域的信息也被一起發送到伺服器。 2 有些時候我們要給用戶一信息,讓他在提交表單時提交上來以確定用戶身份,如sessionkey,等等.當然這些 ...
  • 前言:之前我有寫過CSS3的transform這一這特性,對於它的用法,還不是很透徹,今天補充補充,呵呵 你懂的,小司機準備開車了。 a)再提一提transform的四個屬性 ①旋轉 >rotate(參數a),單位deg,表示旋轉角度,正數順時針,負數逆時針。 ②縮放 >scale(參數a),單位1 ...
  • 下載地址 Vue.js 2.0 參考手冊.CHM下載鏈接: http://pan.baidu.com/s/1kVbhd4b 密碼: wxfh ...
  • ...
  • 前言 啦啦啦~大家好,又見面啦~ 本篇博文講和大家一起完成一個需要註冊、登錄的備忘錄的,一起學習 SharedPreferences 的基本使用,學習 Android 中常見的文件操作方法,複習 Android 界面編程。 直接進入正題~ 基礎知識 1.SharedPreferences 的使用 使 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...