前段时间为公司商城加上了微信支付,中间各种困恼。今天因为改点东西,重拾了出来,就顺便给出一个简单的步骤吧。
一: 定义API, 用于调用API生成预支付订单等
private IWXAPI msgApi;
protected void onCreate(Bundle savedInstanceState) {
msgApi = WXAPIFactory.createWXAPI(BrowserActivity.this, null);
}
二:拿到订单号,价格(记得微信支付单位为1分哦),调用API生成预支付订单
msgApi.registerApp(UtilData.APP_ID);
GetPrepayIdTask getPrepayId = new GetPrepayIdTask(p_order, p_desc, testPrice);
getPrepayId.execute();
三:生成预支付订单后,拿到APP_ID, 预支付订单号等信息封装Post,
req2 = WeiXinGenerate.genPayReq(prepayId);
// Post the pay info
/*关键代码2:提交支付信息*/
msgApi.registerApp(UtilData.APP_ID);
msgApi.sendReq(req2);
四:定义WXPayEntryActivity,(必须是你申请的签名下的wxapi报下。如:com.XX.XX.wxapi,其中comm.XX.XX就是你申请的包)
public void onResp(BaseResp resp) {
if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
PayResp response = (PayResp)resp;
String prePayId = response.prepayId;
switch(resp.errCode){
case 0:
//支付成功 (注,这时候返回的状态并不一定完全正确,处理前最后再调用微信API查询一下result)
break;
case -1:
//支付失败
break;
case -2:
// 支付取消
break;
}
}
}
==============好了关键要调用的步骤,就这几步。接下来补充各种类和方法====================
一: 生成预订单时调用的GetPrepayIdTask类
另注: UtilData.WEIXIN_API="https://api.mch.weixin.qq.com/pay/unifiedorder";
UtilData.APP_ID = 你申请的APP_ID
private class GetPrepayIdTask extends AsyncTask<Void, Void, Map<String,String>> {
private String payOrderId;
private String productDesc;
private int productPrice;
public GetPrepayIdTask(String orderId, String desc, int price){
payOrderId = orderId;
productDesc = desc;
productPrice = price;
}
@Override
protected void onPreExecute() {
dialog = ProgressDialog.show(BrowserActivity.this, "", ""));
}
@Override
protected void onPostExecute(Map<String,String> result) {
if (dialog != null) {
dialog.dismiss();
}
if (result.get("return_code").equalsIgnoreCase("FAIL")) {
// 调用失败,可以给出提示消息
dialog = ProgressDialog.show(BrowserActivity.this, "",""));
dialog.dismiss();
return;
}
String resultCode = result.get("result_code");
if (resultCode.equalsIgnoreCase("SUCCESS")) {
// 成功生成预支付订单
String prepayId = result.get("prepay_id");
UtilData.LAST_PREPAYID = prepayId;
UtilData.LAST_ORDER = payOrderId;
PayReq req2 = new PayReq();
// Generate the pay request
req2 = WeiXinGenerate.genPayReq(prepayId);
// Post the pay info
/*关键代码2:提交支付信息*/
msgApi.registerApp(UtilData.APP_ID);
msgApi.sendReq(req2);
} else if (resultCode.equalsIgnoreCase("FAIL")) {
// 错误处理
String errCode = result.get("err_code");
return;
}
}
@Override
protected Map<String,String> doInBackground(Void... params) {
/*关键代码1:生成预支付订单*/
String entity = WeiXinGenerate.genProductArgs(payOrderId, productDesc, productPrice, UtilData.SHOP_LINK);
byte[] buf = Util.httpPost(UtilData.WEIXIN_API, entity);
String content = new String(buf);
Map<String,String> xml= WeiXinGenerate.decodeXml(content);
return xml;
}
}
二:封装和解析数据的WeiXinGenerate.(大多数来自微信demo中方法的封装). 如下。注:APP_ID, PARTNER_ID,API_KEY为你申请的几个数据。
public class WeiXinGenerate {
/**
* Generate parameters for prepay
* @param description
* @param price
* @return
*/
public static String genProductArgs(String orderId, String description, int price, String notifyUrl) {
try {
String nonceStr = genNonceStr();
List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
packageParams.add(new BasicNameValuePair("appid", UtilData.APP_ID));
packageParams.add(new BasicNameValuePair("body", description));
packageParams.add(new BasicNameValuePair("mch_id", UtilData.PARTNER_ID));
packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
packageParams.add(new BasicNameValuePair("notify_url", notifyUrl));
String outTradeNo = MyMD5.getMessageDigest(orderId.getBytes());
packageParams.add(new BasicNameValuePair("out_trade_no", outTradeNo));//genOutTradNo()));//orderId));//
packageParams.add(new BasicNameValuePair("spbill_create_ip","127.0.0.1"));
packageParams.add(new BasicNameValuePair("total_fee", String.valueOf(price)));
packageParams.add(new BasicNameValuePair("trade_type", "APP"));
String sign = genPackageSign(packageParams);
packageParams.add(new BasicNameValuePair("sign", sign));
String xmlstring =toXml(packageParams);
return new String(xmlstring.getBytes(), "ISO8859-1");
} catch (Exception e) {
return null;
}
}
/**
* Get map object
* @param content
* @return
*/
public static 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){
xml.put(nodeName,parser.nextText());
}
break;
case XmlPullParser.END_TAG:
break;
}
event = parser.next();
}
return xml;
} catch (Exception e) {
Log.e("orion-exception",e.toString());
}
return null;
}
/**
* Generate parameters for pay
* @param prepayId
* @return
*/
public static PayReq genPayReq(String prepayId) {
PayReq req = new PayReq();
req.appId = UtilData.APP_ID;
req.partnerId = UtilData.PARTNER_ID;
req.prepayId = prepayId;
req.packageValue = "Sign=WXPay";
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);
return req;
}
/**
* For order query and close
* @param orderId
* @return
*/
public static String genQueryReq(String orderId) {
try {
String nonceStr = genNonceStr();
List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
packageParams.add(new BasicNameValuePair("appid", UtilData.APP_ID));
packageParams.add(new BasicNameValuePair("mch_id", UtilData.PARTNER_ID));
packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
String outTradeNo = MyMD5.getMessageDigest(orderId.getBytes());
packageParams.add(new BasicNameValuePair("out_trade_no", outTradeNo));
String sign = genPackageSign(packageParams);
packageParams.add(new BasicNameValuePair("sign", sign));
String xmlstring =toXml(packageParams);
return new String(xmlstring.getBytes(), "ISO8859-1");
} catch (Exception e) {
return null;
}
}
/**
* For order query and close
* @param orderId
* @return
*/
public static String genTrackeReq(String orderId) {
try {
String nonceStr = genNonceStr();
List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
packageParams.add(new BasicNameValuePair("appid", UtilData.APP_ID));
packageParams.add(new BasicNameValuePair("mch_id", UtilData.PARTNER_ID));
packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
String outTradeNo = MyMD5.getMessageDigest(orderId.getBytes());
packageParams.add(new BasicNameValuePair("out_trade_no", outTradeNo));
packageParams.add(new BasicNameValuePair("out_refund_no", outTradeNo));
packageParams.add(new BasicNameValuePair("total_fee", String.valueOf(1)));
packageParams.add(new BasicNameValuePair("refund_fee", String.valueOf(1)));
String sign = genPackageSign(packageParams);
packageParams.add(new BasicNameValuePair("sign", sign));
String xmlstring =toXml(packageParams);
return new String(xmlstring.getBytes(), "ISO8859-1");
} catch (Exception e) {
return null;
}
}
private static 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>");
try {
return new String(sb.toString().getBytes(), "ISO8859-1");
} catch (UnsupportedEncodingException e) {
return sb.toString();
}
//return sb.toString();
}
private static 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(UtilData.API_KEY);
String packageSign = MyMD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
return packageSign;
}
private static String genNonceStr() {
Random random = new Random();
return MyMD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
private static long genTimeStamp() {
return System.currentTimeMillis() / 1000;
}
private static 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(UtilData.API_KEY);
String appSign = MyMD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
return appSign;
}
}
三. UtilData类定义的是各种值
public class UtilData {
// Account info
protected static final String APP_ID = "";
protected static final String PARTNER_ID = "";
protected static final String API_KEY="";
// Wei xin api
protected static String WEIXIN_API="https://api.mch.weixin.qq.com/pay/unifiedorder";
protected static String WEIXIN_QUERY="https://api.mch.weixin.qq.com/pay/orderquery";
protected static String WEIXIN_CLOSE="https://api.mch.weixin.qq.com/pay/closeorder";
}
四:另外的工具类Util,MD5Util, MyMD5等其实都是微信demo里给的,没有什么需要预览的。但是不能给附件,只好直接贴出来了
public class Util {
private static final String TAG = "SDK_Sample.Util";
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 class MyMD5 {
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;
}
}
}
=============================应该没有遗漏了,接下来补充几个遇到的问题========================================
一:关于APP_Key
之前以为是AppSecret,结果发现并不是,就是APP_Key同样是32位数字。
二:取消支付,再支付OUT_TRADE_NO_USED
开始以为是要调用close接口,发现调用只好,再提起支付又是“ORDERCLOSED” ->_->。只好先close再后台生成新的订单号支付, 话说有时候却是可以不改订单号,直接再次提交的
三:关于notify_url
测试时也要给外网的notify_url哦,不然调用不到的。。死在这里n久。。。
四:return_code与result_code。
也许并不是每个人同我一样粗心,return_code=success仅表示返回成功,result_code=success才表示业务成功。——然后,notify_url对应的接口里,判断以上两个还没完,trade_state=success才表示成功
=========over 有问题再补充