Android—基于微信開放平臺(tái)v3SDK開發(fā)(微信支付填坑)
接觸微信支付之前聽說(shuō)過(guò)這是一個(gè)坑,,,心里已經(jīng)有了準(zhǔn)備。。。我以為我沒(méi)準(zhǔn)跳坑出不來(lái)了,沒(méi)有想到我填上了,調(diào)用成功之后我感覺(jué)公司所有的同事都是漂亮的,隔著北京的大霧霾我仿佛看見了太陽(yáng)~~~好了,裝逼結(jié)束。。。進(jìn)入正題
開發(fā)準(zhǔn)備:
1.在微信開放平臺(tái)申請(qǐng)賬號(hào)
2.成功后創(chuàng)建應(yīng)用,就是填一些看似很官方很正經(jīng)的資料了。。。(說(shuō)審核7天左右,沒(méi)有意外的情況下你的app第二天就審核成功了是不是很開心,有了appid,是不是就可以調(diào)用微 信支付了????-------想多了,真的)
3.微信支付是需要額外申請(qǐng)的:需要資料審核,賬戶驗(yàn)證,協(xié)議簽署等步驟,(我記得,,資料審核要填寫的東西好多,,,好多,,,賬戶驗(yàn)證就是你審核成功后微信會(huì)發(fā)送郵件到你 注冊(cè)時(shí)登記的郵箱賬號(hào),其中含有隨機(jī)金額用于賬戶驗(yàn)證,協(xié)議簽署,略,太簡(jiǎn)單)一定要好好閱讀你郵件的任何信息,因?yàn)橛械募?xì)節(jié)錯(cuò)了,,,你可能填坑很久。。。。。。
正式開發(fā)階段:
問(wèn)題1:
調(diào)用官方的SDK發(fā)現(xiàn)只能成功的調(diào)起一次微信,再次支付的時(shí)候怎么也調(diào)用不起來(lái)了
解決:
似乎不是什么正經(jīng)方法:在手機(jī)設(shè)置中管理應(yīng)用程序,清除微信數(shù)據(jù),緩存,,再來(lái)一遍,絕對(duì)可以調(diào)起來(lái)(當(dāng)然還是只是一次。。。。)
繼續(xù):
我認(rèn)為要用微信支付嘛,,就只看了調(diào)用支付接口的文檔:
后來(lái)發(fā)現(xiàn)需要的參數(shù)prepayid是怎么也找不到啊,,后來(lái)才發(fā)現(xiàn)這個(gè)prepayid是先調(diào)用”統(tǒng)一下單“這個(gè)接口時(shí)候溫馨反過(guò)來(lái)的東西,但是官方的SDK中并沒(méi)有統(tǒng)一下單的代碼??訂????
所以要先看統(tǒng)一下單文檔了
1.使用自己的APP調(diào)用的時(shí)候把官網(wǎng)down下來(lái)的SDK中WXPayEntryActivity拷貝到自己的項(xiàng)目,所在包的名字最后一定是.wxapi(我連包一起拷了。。。。)
2.在項(xiàng)目清單文件中填寫:
3.SDK中的AppRegister拷貝下來(lái),,,里面換成你自己的appid,然后在項(xiàng)目清單中也注冊(cè)一下。
4.重點(diǎn)來(lái)了,,就是你APP要調(diào)微信支付的activity,我這還叫PayActivity
要調(diào)起微信支付頁(yè)面,要在這個(gè)activity中,將你的app注冊(cè)到微信
接下來(lái)先調(diào)用統(tǒng)一下單獲取prepayid,參數(shù),微信人家要xml格式的!我們就得發(fā)送xml格式的!
好了調(diào)用的時(shí)候把這個(gè)方法的返回值當(dāng)參數(shù)傳,,等待微信返回success吧!。。
我遇見的錯(cuò)誤:簽名錯(cuò)誤
我的原因是大意了 ConfigUtil.NOTIFY_URL這個(gè)參數(shù)寫的空字符串
還有是因?yàn)閐ebug版運(yùn)行的,沒(méi)有打包運(yùn)行
返回成功之后,可以調(diào)用支付接口了
不要忘了這一步去跳轉(zhuǎn)界面,,,,,

沒(méi)有跳轉(zhuǎn)到微信支付可能是你由運(yùn)行的debug版本,,,,沒(méi)有打包。。。。。
下面是我的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注冊(cè)到微信
*/
boolean registerApp = msgApi.registerApp(Constants.APP_ID);
T.show(this, "注冊(cè)========"+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注冊(cè)到微信
*/
check_pay_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
genPayReq();
sendPayReq();
}
});
// // 生成簽名參數(shù)
// 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;
}
/**
* 簽名工具 不含商戶密鑰 -暫時(shí)不用 = * 編碼格式 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轉(zhuǎn)為大寫
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===發(fā)送過(guò)去", 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) {
// 實(shí)例化student對(duì)象
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;
}
/**
* 生成隨機(jī)數(shù)
* @return
*/
private String genNonceStr() {
Random random = new Random();
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
/**
* 時(shí)間戳
* @return
*/
private long genTimeStamp() {
return System.currentTimeMillis() / 1000;
}
/**
* 隨機(jī)數(shù)
* @return
*/
private String genOutTradNo() {
Random random = new Random();
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
/**
* 獲取設(shè)備的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服務(wù)
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;
}
/**
* 轉(zhuǎn)化成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("");
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, "微信跳轉(zhuǎn)====="+sendReq+"");
}
}
</namevaluepair></namevaluepair></namevaluepair></namevaluepair></inetaddress></networkinterface></string,></string,></string,></string,></string,></string,></void,></namevaluepair></namevaluepair></namevaluepair></namevaluepair></string,>
里面沒(méi)有的類,,,,吶:源碼下載。自己下載一下拷貝一下。。。
參考文章:http://www.dbjr.com.cn/article/97712.htm
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android開發(fā)騰訊驗(yàn)證碼遇到的坑
- Android開發(fā)中那些需要注意的坑
- Android開發(fā)手機(jī)無(wú)線調(diào)試的方法
- Android開發(fā)教程之獲取系統(tǒng)輸入法高度的正確姿勢(shì)
- Android開發(fā)解決popupWindow重疊報(bào)錯(cuò)問(wèn)題
- 使用Win10+Android+夜神安卓模擬器,搭建ReactNative開發(fā)環(huán)境
- Android快速開發(fā)系列 10個(gè)常用工具類實(shí)例代碼詳解
- Android開發(fā)筆記之如何正確獲取WebView的網(wǎng)頁(yè)Title
- Android開發(fā)圖片水平旋轉(zhuǎn)180度方法
- Android Studio中使用jni進(jìn)行opencv開發(fā)的環(huán)境配置方法
- Android UI開發(fā)中所遇到的各種坑
相關(guān)文章
Android切換至SurfaceView時(shí)閃屏(黑屏閃一下)以及黑屏移動(dòng)問(wèn)題的解決方法
本文主要介紹了Android切換至SurfaceView時(shí)閃屏(黑屏閃一下)以及黑屏移動(dòng)問(wèn)題的解決方法。具有一定的參考作用,下面跟著小編一起來(lái)看下吧2017-01-01
Android程序報(bào)錯(cuò)程序包org.apache.http不存在問(wèn)題的解決方法
這篇文章主要介紹了Android程序報(bào)錯(cuò)"程序包org.apache.http不存在——Android 6.0已經(jīng)不支持HttpClient" 問(wèn)題的解決方法,感興趣的小伙伴們可以參考一下2016-06-06
Android自定義復(fù)合控件實(shí)現(xiàn)通用標(biāo)題欄
這篇文章主要為大家詳細(xì)介紹了Android自定義復(fù)合控件實(shí)現(xiàn)通用標(biāo)題欄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11
淺談Android studio 生成apk文件時(shí)的 key store path 的問(wèn)題
這篇文章主要介紹了淺談Android studio 生成apk文件時(shí)的 key store path 的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
viewpager+photoview實(shí)現(xiàn)圖片查看器
這篇文章主要為大家詳細(xì)介紹了viewpager+photoview實(shí)現(xiàn)圖片查看器,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
Android入門之使用SharedPreference存取信息詳解
這篇文章主要為大家詳細(xì)介紹了Android如何使用SharedPreference實(shí)現(xiàn)存取信息,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Android有一定的幫助,需要的可以參考一下2022-12-12
Android根據(jù)電話號(hào)碼獲得聯(lián)系人頭像實(shí)例代碼
這篇文章主要介紹了Android根據(jù)電話號(hào)碼獲得聯(lián)系人頭像實(shí)例代碼,是Android程序開發(fā)中非常重要的技巧,需要的朋友可以參考下2014-09-09
Android 如何攔截用戶頻繁操作(點(diǎn)擊事件)
本文主要介紹了Android 如何攔截用戶頻繁操作,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
Android中SurfaceView和view畫出觸摸軌跡
這篇文章主要介紹了Android中SurfaceView和view畫出觸摸軌跡的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-03-03

