先导入jar包libammsdk
调用微信支付
new WXPayUtils(this, "订单描述", 0.01).openWXPay();
接收微信支付的返回结果
在项目下新建一个包wxapi,建立一个类名为WXPayEntryActivity作为接受微信的支付结果(页面自定义),若不想另开页面接收回调可用EventBus进行回调(需用到EventBus包),如下
import com.tencent.mm.sdk.constants.ConstantsAPI; import com.tencent.mm.sdk.modelbase.BaseReq; import com.tencent.mm.sdk.modelbase.BaseResp; import com.tencent.mm.sdk.openapi.IWXAPI; import com.tencent.mm.sdk.openapi.IWXAPIEventHandler; import com.tencent.mm.sdk.openapi.WXAPIFactory; import com.xuhai.tiger.ConstantValues; import com.xuhai.tiger.R; import com.xuhai.tiger.event.WXPayEvent; import de.greenrobot.event.EventBus; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.util.Log; public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler{ private static final String TAG = "MicroMsg.SDKSample.WXPayEntryActivity"; private IWXAPI api; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.pay_result); api = WXAPIFactory.createWXAPI(this, ConstantValues.APP_ID); api.handleIntent(getIntent(), this); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); api.handleIntent(intent, this); } @Override public void onReq(BaseReq req) { } @Override public void onResp(BaseResp resp) { Log.d(TAG, "onPayFinish, errCode = " + resp.errCode); if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) { EventBus.getDefault().post(new WXPayEvent(resp.errCode)); finish(); } } }
WXPayEvent
public class WXPayEvent { private int errCode; public WXPayEvent(int errCode) { this.errCode = errCode; } public int getErrCode() { return errCode; } }
在支付页面获取回调消息并处理
1、先注册
EventBus.getDefault().register(this);
2、接收消息并处理
/* * 微信支付回调 */ public void onEventMainThread(WXPayEvent event){ switch (event.getErrCode()) { case 0: Toast.makeText(this, "支付成功!", Toast.LENGTH_SHORT).show(); break; case -2: Toast.makeText(this, "您取消了订单支付!", Toast.LENGTH_SHORT).show(); break; default: Toast.makeText(this, "支付失败!", Toast.LENGTH_SHORT).show(); break; } }
3、用完必须反注册,否则会造成多次注册而发生多次回调,会产生几个订单
@Override protected void onDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); }
下面是几个必须的页面(类名不要改)
AppRegister
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import com.tencent.mm.sdk.openapi.IWXAPI; import com.tencent.mm.sdk.openapi.WXAPIFactory; import com.xuhai.tiger.ConstantValues; public class AppRegister extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { final IWXAPI api = WXAPIFactory.createWXAPI(context, null); // 将该app注册到微信 api.registerApp(ConstantValues.APP_ID); } }
MD5
import java.security.MessageDigest; public class MD5 { private MD5() {} public final static String getMessageDigest(byte[] buffer) { char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; try { MessageDigest mdTemp = MessageDigest.getInstance("MD5"); mdTemp.update(buffer); byte[] md = mdTemp.digest(); int j = md.length; char str[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; str[k++] = hexDigits[byte0 >>> 4 & 0xf]; str[k++] = hexDigits[byte0 & 0xf]; } return new String(str); } catch (Exception e) { return null; } } }
MD5Util
import java.security.MessageDigest; 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); MessageDigest md = MessageDigest.getInstance("MD5"); if (charsetname == null || "".equals(charsetname)) resultString = byteArrayToHexString(md.digest(resultString .getBytes())); else resultString = byteArrayToHexString(md.digest(resultString .getBytes(charsetname))); } catch (Exception exception) { } return resultString; } private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" }; }
Util
import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.net.Socket; import java.net.UnknownHostException; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.HttpVersion; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpParams; import org.apache.http.params.HttpProtocolParams; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import junit.framework.Assert; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Bitmap.CompressFormat; import android.util.Log; public class Util { private static final String TAG = "SDK_Sample.Util"; public static byte[] bmpToByteArray(final Bitmap bmp, final boolean needRecycle) { ByteArrayOutputStream output = new ByteArrayOutputStream(); bmp.compress(CompressFormat.PNG, 100, output); if (needRecycle) { bmp.recycle(); } byte[] result = output.toByteArray(); try { output.close(); } catch (Exception e) { e.printStackTrace(); } return result; } public static byte[] httpGet(final String url) { if (url == null || url.length() == 0) { Log.e(TAG, "httpGet, url is null"); return null; } HttpClient httpClient = getNewHttpClient(); HttpGet httpGet = new HttpGet(url); try { HttpResponse resp = httpClient.execute(httpGet); if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { Log.e(TAG, "httpGet fail, status code = " + resp.getStatusLine().getStatusCode()); return null; } return EntityUtils.toByteArray(resp.getEntity()); } catch (Exception e) { Log.e(TAG, "httpGet exception, e = " + e.getMessage()); e.printStackTrace(); return null; } } public static byte[] httpPost(String url, String entity) { if (url == null || url.length() == 0) { Log.e(TAG, "httpPost, url is null"); return null; } HttpClient httpClient = getNewHttpClient(); HttpPost httpPost = new HttpPost(url); try { httpPost.setEntity(new StringEntity(entity)); httpPost.setHeader("Accept", "application/json"); httpPost.setHeader("Content-type", "application/json"); HttpResponse resp = httpClient.execute(httpPost); if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { Log.e(TAG, "httpGet fail, status code = " + resp.getStatusLine().getStatusCode()); return null; } return EntityUtils.toByteArray(resp.getEntity()); } catch (Exception e) { Log.e(TAG, "httpPost exception, e = " + e.getMessage()); e.printStackTrace(); return null; } } private static class SSLSocketFactoryEx extends SSLSocketFactory { SSLContext sslContext = SSLContext.getInstance("TLS"); public SSLSocketFactoryEx(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { super(truststore); TrustManager tm = new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException { } }; sslContext.init(null, new TrustManager[] { tm }, null); } @Override public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException { return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose); } @Override public Socket createSocket() throws IOException { return sslContext.getSocketFactory().createSocket(); } } private static HttpClient getNewHttpClient() { try { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); trustStore.load(null, null); SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore); sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); HttpParams params = new BasicHttpParams(); HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); HttpProtocolParams.setContentCharset(params, HTTP.UTF_8); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80)); registry.register(new Scheme("https", sf, 443)); ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry); return new DefaultHttpClient(ccm, params); } catch (Exception e) { return new DefaultHttpClient(); } } public static byte[] readFromFile(String fileName, int offset, int len) { if (fileName == null) { return null; } File file = new File(fileName); if (!file.exists()) { Log.i(TAG, "readFromFile: file not found"); return null; } if (len == -1) { len = (int) file.length(); } Log.d(TAG, "readFromFile : offset = " + offset + " len = " + len + " offset + len = " + (offset + len)); if(offset <0){ Log.e(TAG, "readFromFile invalid offset:" + offset); return null; } if(len <=0 ){ Log.e(TAG, "readFromFile invalid len:" + len); return null; } if(offset + len > (int) file.length()){ Log.e(TAG, "readFromFile invalid file len:" + file.length()); return null; } byte[] b = null; try { RandomAccessFile in = new RandomAccessFile(fileName, "r"); b = new byte[len]; in.seek(offset); in.readFully(b); in.close(); } catch (Exception e) { Log.e(TAG, "readFromFile : errMsg = " + e.getMessage()); e.printStackTrace(); } return b; } private static final int MAX_DECODE_PICTURE_SIZE = 1920 * 1440; public static Bitmap extractThumbNail(final String path, final int height, final int width, final boolean crop) { Assert.assertTrue(path != null && !path.equals("") && height > 0 && width > 0); BitmapFactory.Options options = new BitmapFactory.Options(); try { options.inJustDecodeBounds = true; Bitmap tmp = BitmapFactory.decodeFile(path, options); if (tmp != null) { tmp.recycle(); tmp = null; } Log.d(TAG, "extractThumbNail: round=" + width + "x" + height + ", crop=" + crop); final double beY = options.outHeight * 1.0 / height; final double beX = options.outWidth * 1.0 / width; Log.d(TAG, "extractThumbNail: extract beX = " + beX + ", beY = " + beY); options.inSampleSize = (int) (crop ? (beY > beX ? beX : beY) : (beY < beX ? beX : beY)); if (options.inSampleSize <= 1) { options.inSampleSize = 1; } // NOTE: out of memory error while (options.outHeight * options.outWidth / options.inSampleSize > MAX_DECODE_PICTURE_SIZE) { options.inSampleSize++; } int newHeight = height; int newWidth = width; if (crop) { if (beY > beX) { newHeight = (int) (newWidth * 1.0 * options.outHeight / options.outWidth); } else { newWidth = (int) (newHeight * 1.0 * options.outWidth / options.outHeight); } } else { if (beY < beX) { newHeight = (int) (newWidth * 1.0 * options.outHeight / options.outWidth); } else { newWidth = (int) (newHeight * 1.0 * options.outWidth / options.outHeight); } } options.inJustDecodeBounds = false; Log.i(TAG, "bitmap required size=" + newWidth + "x" + newHeight + ", orig=" + options.outWidth + "x" + options.outHeight + ", sample=" + options.inSampleSize); Bitmap bm = BitmapFactory.decodeFile(path, options); if (bm == null) { Log.e(TAG, "bitmap decode failed"); return null; } Log.i(TAG, "bitmap decoded size=" + bm.getWidth() + "x" + bm.getHeight()); final Bitmap scale = Bitmap.createScaledBitmap(bm, newWidth, newHeight, true); if (scale != null) { bm.recycle(); bm = scale; } if (crop) { final Bitmap cropped = Bitmap.createBitmap(bm, (bm.getWidth() - width) >> 1, (bm.getHeight() - height) >> 1, width, height); if (cropped == null) { return bm; } bm.recycle(); bm = cropped; Log.i(TAG, "bitmap croped size=" + bm.getWidth() + "x" + bm.getHeight()); } return bm; } catch (final OutOfMemoryError e) { Log.e(TAG, "decode bitmap failed: " + e.getMessage()); options = null; } return null; } public static String sha1(String str) { if (str == null || str.length() == 0) { return null; } char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; try { MessageDigest mdTemp = MessageDigest.getInstance("SHA1"); mdTemp.update(str.getBytes()); byte[] md = mdTemp.digest(); int j = md.length; char buf[] = new char[j * 2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; buf[k++] = hexDigits[byte0 >>> 4 & 0xf]; buf[k++] = hexDigits[byte0 & 0xf]; } return new String(buf); } catch (Exception e) { return null; } } public static List<String> stringsToList(final String[] src) { if (src == null || src.length == 0) { return null; } final List<String> result = new ArrayList<String>(); for (int i = 0; i < src.length; i++) { result.add(src[i]); } return result; } }
WXPayUtils
import java.io.StringReader; 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 android.app.ProgressDialog; import android.os.AsyncTask; import android.util.Log; import android.util.Xml; import com.tencent.mm.sdk.modelpay.PayReq; import com.tencent.mm.sdk.openapi.IWXAPI; import com.tencent.mm.sdk.openapi.WXAPIFactory; import com.xuhai.tiger.ConstantValues; import com.xuhai.tiger.view.activity.BaseActivity; public class WXPayUtils { private static final String TAG = "WXPayUtils"; private BaseActivity mContext; private String price;//商品价格 private String description;//商品描述 private PayReq req; private final IWXAPI msgApi; private Map<String,String> resultunifiedorder; private StringBuffer sb; public WXPayUtils(BaseActivity context,String description,float price) { this.mContext = context; this.description = description; this.price = String.valueOf((int)(price*100));//微信支付单位是分,参数必须是整数,因此乘以100再转成整数,转换后单价是元 msgApi = WXAPIFactory.createWXAPI(context, null); req = new PayReq(); sb=new StringBuffer(); msgApi.registerApp(ConstantValues.APP_ID); } public void openWXPay(){ GetPrepayIdTask getPrepayId = new GetPrepayIdTask(); getPrepayId.execute(); } /** *生成签名 */ 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(ConstantValues.API_KEY); String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase(); Log.e("orion",packageSign); return packageSign; } 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(ConstantValues.API_KEY); this.sb.append("sign str "+sb.toString()+" "); String appSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase(); 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(mContext, "", "提交订单信息中,请稍后...",true); } @Override protected void onPostExecute(Map<String,String> result) { sb.append("prepay_id "+result.get("prepay_id")+" "); resultunifiedorder=result; genPayReq(); sendPayReq(); if (dialog != null) { dialog.dismiss(); } } @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; } private String genNonceStr() { Random random = new Random(); return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes()); } private long genTimeStamp() { return System.currentTimeMillis() / 1000; } private String genOutTradNo() { Random random = new Random(); return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes()); } // private String genProductArgs() { StringBuffer xml = new StringBuffer(); try { String nonceStr = genNonceStr(); xml.append("</xml>"); //详见https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_1 List<NameValuePair> packageParams = new LinkedList<NameValuePair>(); packageParams.add(new BasicNameValuePair("appid", ConstantValues.APP_ID)); packageParams.add(new BasicNameValuePair("body", description)); packageParams.add(new BasicNameValuePair("mch_id", ConstantValues.MCH_ID)); packageParams.add(new BasicNameValuePair("nonce_str", nonceStr)); packageParams.add(new BasicNameValuePair("notify_url", "http://121.40.35.3/test")); packageParams.add(new BasicNameValuePair("out_trade_no",genOutTradNo())); packageParams.add(new BasicNameValuePair("spbill_create_ip","127.0.0.1")); packageParams.add(new BasicNameValuePair("total_fee", price)); packageParams.add(new BasicNameValuePair("trade_type", "APP")); String sign = genPackageSign(packageParams); packageParams.add(new BasicNameValuePair("sign", sign)); String xmlstring =toXml(packageParams); //改变拼接之后xml字符串格式,解决body中文签名时错误的问题 return new String(xmlstring.toString().getBytes(), "ISO8859-1"); } catch (Exception e) { Log.e(TAG, "genProductArgs fail, ex = " + e.getMessage()); return null; } } private void genPayReq() { /* 详见https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_12 */ req.appId = ConstantValues.APP_ID;//微信分配的公众账号ID req.partnerId = ConstantValues.MCH_ID;//微信支付分配的商户号 req.prepayId = resultunifiedorder.get("prepay_id");//微信返回的支付交易会话ID req.packageValue = "Sign=WXPay";//暂填写固定值Sign=WXPay req.nonceStr = genNonceStr();//随机字符串,不长于32位。推荐随机数生成算法 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 "+req.sign+" "); Log.e("orion", signParams.toString()); } private void sendPayReq() { msgApi.registerApp(ConstantValues.APP_ID); msgApi.sendReq(req); } }
下面是微信商户的配置信息
public class ConstantValues { /** 微信支付 */ //请同时修改 androidmanifest.xml里面,.PayActivityd里的属性<data android:scheme="wxb4ba3c02aa476ea1"/>为新设置的appid public static final String APP_ID = ""; //商户号 public static final String MCH_ID = ""; //API密钥,在商户平台设置 public static final String API_KEY=""; }
另外,在Android Manifest.xml 里面添加以下代码
<!-- 微信支付回调页面--> <activity android:name=".wxapi.WXPayEntryActivity" android:exported="true" android:launchMode="singleTop"/> <receiver android:name=".AppRegister"> <intent-filter> <action android:name="com.tencent.mm.plugin.openapi.Intent.ACTION_REFRESH_WXAPP" /> </intent-filter> </receiver>
注意:App的包名与应用签名必须与商户申请时的一样,否则永远无法调出微信支付
如何查看应用签名(需安装微信提供的应用签名查看工具)
1、在手机上安装已经过签名的apk
2、安装此apk
3、打开此apk输入包名确定即可查看应用签名
ps:包名查看(AndroidManifest.xml文件,package="xxx.xxx.xxx")