随着电商时代的发展,微信成为主流,微信支付也随之到来,小生开发步骤如下:
1.申请微信开放平台账号,开通微信支付,会获取到AppID,AppSecret,PaySignKey,PartnerID,PartnerKey(后来修改后,少了某一个参数,后面在更新吧)
提前都准备好了,接下来是代码阶段。
2.验证微信是否支持微信支付。
<span style="white-space:pre"> </span>// 微信是否支持支付
public static boolean isPay(Context context) {
<span style="font-family: Arial, Helvetica, sans-serif;">IWXAPI </span><span style="font-family: Arial, Helvetica, sans-serif;">api = WXAPIFactory.createWXAPI(context, AppID, false);</span>
boolean isPaySupported = api.getWXAppSupportAPI() >= TIMELINE_SUPPORTED_VERSION;
return isPaySupported;
}
3.获取微信支付的token
private static class GetAccessTokenTask extends
AsyncTask<Void, Void, GetAccessTokenResult> {
@Override
protected void onPostExecute(GetAccessTokenResult result) {
if (result.localRetCode == LocalRetCode.ERR_OK) {
Editor editor = context.getSharedPreferences(Pref.PY6,
Context.MODE_PRIVATE).edit();
editor.putString("accessToken", result.accessToken).commit();
editor.putInt("accessToken_time",
Integer.parseInt(String.valueOf(Util.genTimeStamp())))
.commit();
GetPrepayIdTask getPrepayId = new GetPrepayIdTask(
result.accessToken);
getPrepayId.execute();
} else {
if (dialog != null) {
dialog.dismiss();
}
context.getSharedPreferences(Pref.PY6, Context.MODE_PRIVATE)
.edit().remove("accessToken").commit();
context.getSharedPreferences(Pref.PY6, Context.MODE_PRIVATE)
.edit().remove("accessToken_time").commit();
Toast.makeText(context, R.string.system_fail,
Toast.LENGTH_SHORT).show();
}
}
@Override
protected GetAccessTokenResult doInBackground(Void... params) {
GetAccessTokenResult result = new GetAccessTokenResult();
String url = String
.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=%s&appid=%s&secret=%s",
"client_credential", AppID, AppSecret);
byte[] buf = Util.httpGet(url);
if (buf == null || buf.length == 0) {
result.localRetCode = LocalRetCode.ERR_HTTP;
return result;
}
String content = new String(buf);
result.parseFrom(content);
return result;
}
}
4. 获取PrepayId
private static class GetPrepayIdTask extends
AsyncTask<Void, Void, GetPrepayIdResult> {
private String accessT;
public GetPrepayIdTask(String accessToken) {
this.accessT = accessToken;
}
@Override
protected void onPreExecute() {
}
@Override
protected void onPostExecute(GetPrepayIdResult result) {
if (result.localRetCode == LocalRetCode.ERR_OK) {
sendPayReq(result);
} else {
if (dialog != null) {
dialog.dismiss();
}
context.getSharedPreferences(Pref.PY6, Context.MODE_PRIVATE)
.edit().remove("accessToken").commit();
context.getSharedPreferences(Pref.PY6, Context.MODE_PRIVATE)
.edit().remove("accessToken_time").commit();
Toast.makeText(context, R.string.system_fail,
Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onCancelled() {
super.onCancelled();
}
@Override
protected GetPrepayIdResult doInBackground(Void... params) {
String url = String.format(
"https://api.weixin.qq.com/pay/genprepay?access_token=%s",
accessT);
Bundle b = Util.genProductArgs(body, money, traceId, AppID,
PartnerID, PaySignKey, PartnerKey, moneye);
timeStamp = b.getLong("timeStamp");
String entity = b.getString("json");
packageValue = b.getString("packageValue");
nonceStr = b.getString("nonceStr");
GetPrepayIdResult result = new GetPrepayIdResult();
byte[] buf = Util.httpPost(url, entity);
if (buf == null || buf.length == 0) {
result.localRetCode = LocalRetCode.ERR_HTTP;
return result;
}
String content = new String(buf);
result.parseFrom(content);
return result;
}
}
private static class GetPrepayIdResult {
<span style="white-space:pre"> </span>public LocalRetCode localRetCode = LocalRetCode.ERR_OTHER;
<span style="white-space:pre"> </span>public String prepayId;
<span style="white-space:pre"> </span>public int errCode;
<span style="white-space:pre"> </span>public String errMsg;
<span style="white-space:pre"> </span>public void parseFrom(String content) {
<span style="white-space:pre"> </span>if (content == null || content.length() <= 0) {
<span style="white-space:pre"> </span>localRetCode = LocalRetCode.ERR_JSON;
<span style="white-space:pre"> </span>return;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>try {
<span style="white-space:pre"> </span>JSONObject json = new JSONObject(content);
<span style="white-space:pre"> </span>if (json.has("prepayid")) { // success case
<span style="white-space:pre"> </span>prepayId = json.getString("prepayid");
<span style="white-space:pre"> </span>localRetCode = LocalRetCode.ERR_OK;
<span style="white-space:pre"> </span>} else {
<span style="white-space:pre"> </span>localRetCode = LocalRetCode.ERR_JSON;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>errCode = json.getInt("errcode");
<span style="white-space:pre"> </span>errMsg = json.getString("errmsg");
<span style="white-space:pre"> </span>} catch (Exception e) {
<span style="white-space:pre"> </span>localRetCode = LocalRetCode.ERR_JSON;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>private static enum LocalRetCode {
<span style="white-space:pre"> </span>ERR_OK, ERR_HTTP, ERR_JSON, ERR_OTHER
<span style="white-space:pre"> </span>}
5.调起微信支付,在此一定要打包支付,打包用的key必须和生成签名的MD5所用的key是一样的,而且每次测试必须打包在测试。
// 第三步: 调起微信支付 在WXPayEntryActivity 接受微信支付返回的结果
private static void sendPayReq(GetPrepayIdResult result) {
PayReq req = new PayReq();
req.appId = AppID;
req.partnerId = PartnerID;
req.prepayId = result.prepayId;
req.nonceStr = nonceStr;
req.timeStamp = String.valueOf(timeStamp);
req.packageValue = "Sign=" + packageValue;
// req.packageValue = "Sign=WXPay";
List<NameValuePair> signParams = new LinkedList<NameValuePair>();
signParams.add(new BasicNameValuePair("appid", req.appId));
signParams.add(new BasicNameValuePair("appkey", PaySignKey));
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 = Util.genSign(signParams);
// 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信
boolean b = api.sendReq(req);
// System.out.println(sb.toString());
if (dialog != null) {
dialog.dismiss();
}
}
6.微信支付中间需要的方法 ,签名,包名等
public class Util {
public static final String TAG = "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;
}
}
public 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();
}
}
public 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;
}
public 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;
}
public static String genNonceStr() {
Random random = new Random();
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
public static long genTimeStamp() {
return System.currentTimeMillis() / 1000;
}
/**
* 建议 traceid 字段包含用户信息及订单信息,方便后续对订单状态的查询和跟踪
*/
public static String getTraceId() {
return "crestxu_" + genTimeStamp();
}
/**
* 注意:商户系统内部的订单号,32个字符内、可包含字母,确保在商户系统唯一
*/
public static String genOutTradNo() {
Random random = new Random();
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
public static String genSign(List<NameValuePair> params) {
StringBuilder sb = new StringBuilder();
int i = 0;
for (; i < params.size() - 1; i++) {
sb.append(params.get(i).getName());
sb.append('=');
sb.append(params.get(i).getValue());
sb.append('&');
}
sb.append(params.get(i).getName());
sb.append('=');
sb.append(params.get(i).getValue());
String sha1 = Util.sha1(sb.toString());
Log.d(TAG, "genSign, sha1 = " + sha1);
return sha1;
}
public static String genPackage(List<NameValuePair> params, String PartnerKey) {
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(PartnerKey);
String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
return URLEncodedUtils.format(params, "UTF-8") + "&sign=" + packageSign;
}
public static Bundle genProductArgs(String body, String money, String traceId, String AppID, String PartnerID,
String PaySignKey, String PartnerKey, String mone) {
Bundle b = new Bundle();
JSONObject json = new JSONObject();
long timeStamp;
String packageValue;
String nonceStr;
try {
json.put("appid", AppID);
// String traceId = getTraceId();
json.put("traceid", traceId);
nonceStr = genNonceStr();
json.put("noncestr", nonceStr);
List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
packageParams.add(new BasicNameValuePair("attach", mone)); // TODO 余额
packageParams.add(new BasicNameValuePair("bank_type", "WX"));
packageParams.add(new BasicNameValuePair("body", body.replaceAll(" ", ""))); // 订单内容
packageParams.add(new BasicNameValuePair("fee_type", "1")); // 支付币种 暂时只有1
packageParams.add(new BasicNameValuePair("input_charset", "UTF-8"));
// packageParams.add(new BasicNameValuePair("notify_url", "http://www.py6.com/api/payment/weixin/")); // 正式
// packageParams.add(new BasicNameValuePair("notify_url", "http://weixin.qq.com"));
packageParams.add(new BasicNameValuePair("notify_url", "http://www.py6.com/api/payment/paytest/wxpay.php?act=wxpay&op=0pay")); // 测试
packageParams.add(new BasicNameValuePair("out_trade_no", traceId));
packageParams.add(new BasicNameValuePair("partner", PartnerID));
packageParams.add(new BasicNameValuePair("spbill_create_ip", "192.168.1.1"));
packageParams.add(new BasicNameValuePair("total_fee", money)); // TODO 订单总价钱
packageValue = genPackage(packageParams, PartnerKey);
json.put("package", packageValue);
timeStamp = genTimeStamp();
json.put("timestamp", timeStamp);
List<NameValuePair> signParams = new LinkedList<NameValuePair>();
signParams.add(new BasicNameValuePair("appid", AppID));
signParams.add(new BasicNameValuePair("appkey", PaySignKey));
signParams.add(new BasicNameValuePair("noncestr", nonceStr));
signParams.add(new BasicNameValuePair("package", packageValue));
signParams.add(new BasicNameValuePair("timestamp", String.valueOf(timeStamp)));
signParams.add(new BasicNameValuePair("traceid", traceId));
json.put("app_signature", genSign(signParams));
json.put("sign_method", "sha1");
} catch (Exception e) {
Log.e(TAG, "genProductArgs fail, ex = " + e.getMessage());
return null;
}
b.putString("json", json.toString());
b.putLong("timeStamp", timeStamp);
b.putString("packageValue", packageValue);
b.putString("nonceStr", nonceStr);
return b;
}
}
7.遇到类似8位数字的报错 可以找wepayTS@tenc