第二次做微信支付了,第一次客户端服务端全是自己摸索着做成功的,这一次公司的后台没有微信支付的经验,并且用的是python,不是java,我就本地做了个demo给他演示整个支付的流程,在这里也做下记录,便于以后查看。
其实微信支付的流程就几个步骤,
1.app向自己应用的服务器发一个请求,把商品的id,个数,用户标识等参数传递过去,
2.我们的服务器收到后做一些签名处理发送给微信服务器,得到一个预付订单号的参数,
3.将客户端需要的支付参数返回过去,
4.app本地再调用起调用本地微信app支付的操作,
5.最后我们应用的服务端会接受微信服务器的异步通知支付结果来更新数据库的订单状态
将微信支付的sdk粘贴的项目的libs目录下并依赖到项目中,如果没有请到微信支付开放平台下载:
build.gradle依赖:
// 微信支付
implementation files('libs/libammsdk.jar')
我这里贴一下手机本地模拟服务器的操作然后调起支付的流程:
class VipPayActivity : Activity() {
//微信支付
private var api: IWXAPI? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView()
api = WXAPIFactory.createWXAPI(this, Constants.APP_ID, false)
api?.registerApp(Constants.APP_ID)
btn_sureOrderPay.setOnClickListener {
Thread(Runnable {
//1.模拟服务器生成请求预付订单Prepay_id
val orderSn = OrderInfoUtil2_0.getOutTradeNo()// 商品订单号
val nonceStr = OrderInfoUtil2_0.getOutTradeNo()
val WX_PAY_CALLBACK = "http://123.16.20.98:8080/validateWxpayInfo"// 异步回调api
// val WX_PAY_CALLBACK = ""// 异步回调api
// String openid = parm.get("openid");//支付用户openid
val WX_UNIFIEDORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder"// 统一下单微信api
val map = HashMap<String, String>()
var ip = "123.12.12.123"
// if (StringUtils.StringIsNull(PhoneIpUtil.getPhoneIp(this))) run {
// ip = PhoneIpUtil.getPhoneIp(this)
// }
// 加密,这里只列举必填字段
map.put("body", "支付测试");// 商品描述
map.put("mch_id", Constants.MCHID);// 商户平台id
map.put("appid", Constants.APP_ID);// 公众号id
map.put("nonce_str", nonceStr);// 随机字符串
map.put("notify_url", WX_PAY_CALLBACK);// 异步回调api
map.put("spbill_create_ip", ip);// 支付ip
map.put("out_trade_no", orderSn);// 商品订单号
// 微信支付官方文档要求支付金额的单位必须是分,而不是元,但是为了和支付宝统一,
// 客户端统一以(人民币)元为单位,微信的到服务端后再转,客户端不用管
// 实际支付的金额,订单总金额,单位为分,只能为整数,详见支付金额,实际项目中应该是根据订单号在自身系统查询后获得
map.put("total_fee", "1");
map.put("trade_type", "APP"); // APP调用
// map.put("openid", openid);//支付用户openid
var sign = WxPaySignatureUtils.getSign(map, Constants.APP_KEY) // 签名(该签名应使用微信商户平台的API证书中的密匙key)
var xml:String = "<xml>" + "<appid>" +Constants.APP_ID + "</appid>"+ "<body>"+ "支付测试" + "</body>" + "<mch_id>" + Constants.MCHID + "</mch_id>"+ "<nonce_str>" + nonceStr+ "</nonce_str>"+ "<notify_url>"+ WX_PAY_CALLBACK+ "</notify_url>"+
// "<openid>"+ openid +"</openid>"+
"<out_trade_no>" + orderSn + "</out_trade_no>"+ "<spbill_create_ip>" + ip + "</spbill_create_ip>"+ "<total_fee>" + "1" + "</total_fee>"+ "<trade_type>APP</trade_type>" + "<sign>" + sign + "</sign>"+ "</xml>";
// xml = String(xml.toByteArray(), Charset.forName("ISO8859-1"))
//微信支付接口签名校验工具网页地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=20_1 可以把你的xml拿到这个地方进行校验
btn_sureOrderPay.post(Runnable {
LogUtils.e(TAG,"发送给微信的报文:" + xml)
})
// LogUtils.e(TAG,"加密后的的签名字符串:" + sign);
// 请求
var response = "";
try {
response = WXHttpUtil.doPostString(xml, WX_UNIFIEDORDER)
btn_sureOrderPay.post(Runnable {
LogUtils.e(TAG,"请求/pay/unifiedorder下单接口后返回数据:" + response)
})
// 处理请求结果
if (StringUtils.StringIsNull(response)){
var order = WxPayParamBean()
var date:WxPayParamBean.Data = WxPayParamBean.Data()
order.date = date
var resultMap = HashMap<String, Object>()
resultMap = WxPaySignatureUtils.getMapFromXML(response) as HashMap<String, Object>;// 解析返回值
if ("SUCCESS".equals(resultMap.get("return_code").toString())
&& "SUCCESS".equals(resultMap.get("result_code").toString())) {
// 这里和普通的接口调用不同,使用的是sdkExecute
// System.out.println("orderString"+response.getBody());//就是orderString
// 可以直接给客户端请求,无需再做处理。
val parameterMap = TreeMap<String, String>()
parameterMap.put("appid", Constants.APP_ID)
parameterMap.put("partnerid", Constants.MCHID)
parameterMap.put("prepayid",
resultMap["prepay_id"] as String)
parameterMap.put("package", "Sign=WXPay")
parameterMap.put("noncestr", nonceStr)
parameterMap.put("timestamp",
System.currentTimeMillis().toString())
val sign1 = WxPaySignatureUtils.getSign(parameterMap,
Constants.APP_KEY)
parameterMap.put("sign", sign1)
// 生成返回给app的微信支付订单信息
order.date.appid=Constants.APP_ID
order.date.noncestr=nonceStr
order.date.package_str="Sign=WXPay"
order.date.mchid=Constants.MCHID
order.date.prepayid=resultMap["prepay_id"] as String
order.date.sign=sign1
order.date.timestamp =java.lang.Long.toString(System.currentTimeMillis())
LogUtils.e(TAG,"微信支付统一下单请求成功,获得的Prepay_id是:"
// + order.date.mchid);
+ order.toString())
btn_sureOrderPay.post(Runnable {
Toast.makeText(this,"获得的Prepay_id是=="+order.date.prepayid,Toast.LENGTH_SHORT).show()
})
//2.app发起支付请求
val req = PayReq()
//req.appId = "wxf8b4f85f3a794e77"; // 测试用appId
req.appId = Constants.APP_ID
req.partnerId = Constants.MCHID
req.prepayId = order.date.prepayid
req.nonceStr = order.date.noncestr
req.timeStamp = order.date.timestamp
req.packageValue = order.date.package_str
req.sign = order.date.sign
// req.extData = "app data"; // optional
// ToastUtil.ShortToast(data.toString());
// 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信
// api?.registerApp(Constants.APP_ID);
api?.sendReq(req)
btn_sureOrderPay.post(Runnable {
Toast.makeText(this, "正常调起支付", Toast.LENGTH_SHORT).show()
})
}else{
btn_sureOrderPay.post(Runnable {
LogUtils.e(TAG,"微信支付统一下单请求失败")
})
}
}else{
btn_sureOrderPay.post(Runnable {
LogUtils.e(TAG,"微信支付统一下单请求失败response==null")
})
}
} catch ( e:Exception) {
LogUtils.e(TAG,"微信支付统一下单请求失败e=="+e.message)
}
}).start()
btn_sureOrderPay.isEnabled = true
}
}
}
布局的代码就不贴了,随便放一个按钮点击事件就行
目录下创建wxapi目录,并创建对应的类
清单文件注册对应的activity ,WXEntryActivity微信登陆用到,WXPayEntryActivity是微信支付时会用到的,
PayActivity你自己的调起支付页面
<activity
android:name=".component.activity.PayActivity"
android:screenOrientation="portrait">
<!--这个intent-filter不要忘了,里面填微信申请的appid 微信支付用-->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="wx9e8fbtututtu67676767u26" />
</intent-filter>
</activity>
<!--微信支付 微信登陆会掉-->
<activity
android:name=".wxapi.WXEntryActivity"
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTop" />
<activity
android:name=".wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleTop" />
贴下微信支付结果回调的WXPayEntryActivity的代码:
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {
private static final String TAG = "WXPayEntryActivity";
private IWXAPI api;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pay_result);
api = WXAPIFactory.createWXAPI(this, Constants.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 baseReq) {
}
@SuppressLint("NewApi")
@Override
public void onResp(BaseResp resp) {
Log.d(TAG, "onPayFinish, errCode = " + resp.errCode);
if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.app_tip);
String result = "";
if (resp.errCode==0){
result = "支付成功,点击任意地方即退出关闭当前页面";
// 手机本地做购买过vip的标识
SharedPreferencesUnitls.setParam(this,"isBuyVip","true");
}else if (resp.errCode==-1){
result = "支付异常,请清理微信缓存,点击任意地方即退出关闭当前页面";
}else if (resp.errCode==-2){
result = "支付取消,点击任意地方即退出关闭当前页面";
}
builder.setMessage(getString(R.string.pay_result_callback_msg, result));
// builder.setNegativeButton("返回重新支付", new DialogInterface.OnClickListener() {
// @Override
// public void onClick(DialogInterface dialog, int which) {
// finish();
// }
// });
// builder.setNeutralButton("取消支付", new DialogInterface.OnClickListener() {
// @Override
// public void onClick(DialogInterface dialog, int which) {
// finish();
// }
// });
builder.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
if (!WXPayEntryActivity.this.isDestroyed()){
finish();
}
}
});
builder.show();
}
}
/**
* 获取openid accessToken值用于后期操作
*
* @param code 请求码
*/
// private void getAccess_token(final String code) {
// String path = "https://api.weixin.qq.com/sns/oauth2/access_token";
// Map<String, String> params = new HashMap<>();
// params.put("appid", Constants.APP_ID);
// params.put("secret", Constants.AppSecret);
// params.put("code", code);
// params.put("grant_type", "authorization_code");
// OkHttpUtils.post()
// .url(path)
// .params(params)
// .build()
// .execute(new StringCallback() {
// @Override
// public void onError(Call call, Exception e) {
//
// }
//
// @Override
// public void onResponse(String response) {
// LogUtil.i("TAG", "response == " + response);
// JSONObject jsonObject = null;
// try {
// jsonObject = new JSONObject(response);
// String openid = jsonObject.getString("openid").toString().trim();
// String access_token = jsonObject.getString("access_token").toString().trim();
checkThird(openid, access_token);
// } catch (JSONException e) {
// e.printStackTrace();
// }
// }
// });
// }
}
以及它的pay_result.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/NavPage">
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:textColor="#ff000000"
android:textSize="20dp"
android:text="@string/pay_result_tip"/>
</LinearLayout>
再贴下几个相关类的代码:
public class Constants {
//微信支付
// APP_ID 替换为你的应用从官方网站申请到的合法appId
public static final String APP_ID = "wx9e8f333333eaacea26";
public static final String AppSecret = "a5d34t44yy4y4y9ac647d1f61bbe";
public static final String MCHID = "152241333331"; // 商户id
public static final String APP_KEY = "61304t4t4t4y4y4ybcca6fe5296df9c"; // key,正式支付必须放到服务端
}
/**
* Md5校验工具类
*
* @author Fengwx
*/
public class MD5Util {
/**
* MD5校验字符串
*
* @param s
* @return
*/
public static String MD5(String s) {
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F'};
try {
byte[] btInput = s.getBytes();
// 获得MD5摘要算法的 MessageDigest 对象
MessageDigest mdInst = MessageDigest.getInstance("MD5");
// 使用指定的字节更新摘要
mdInst.update(btInput);
// 获得密文
byte[] md = mdInst.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) {
e.printStackTrace();
return null;
}
}
/**
* MD5校验字节数组
*
* @param b
* @return
*/
public static String MD5(byte[] b) {
String s = null;
char hexDigits[] = { // 用来将字节转换成 16 进制表示的字符
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D',
'E', 'F'};
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(b);
byte tmp[] = md.digest(); // MD5 的计算结果是一个 128 位的长整数,
// 用字节表示就是 16 个字节
char str[] = new char[16 * 2]; // 每个字节用 16 进制表示的话,使用两个字符,
// 所以表示成 16 进制需要 32 个字符
int k = 0; // 表示转换结果中对应的字符位置
for (int i = 0; i < 16; i++) { // 从第一个字节开始,对 MD5 的每一个字节
// 转换成 16 进制字符的转换
byte byte0 = tmp[i]; // 取第 i 个字节
str[k++] = hexDigits[byte0 >>> 4 & 0xf]; // 取字节中高 4 位的数字转换, >>>,
// 逻辑右移,将符号位一起右移
str[k++] = hexDigits[byte0 & 0xf]; // 取字节中低 4 位的数字转换
}
s = new String(str); // 换后的结果转换为字符串
} catch (Exception e) {
e.printStackTrace();
}
return s;
}
/**
* 生成16位md5码
*
* @param md5Str
* @return
*/
public static String to16bitMd5(String md5Str) {
String result = "";
try {
MessageDigest algorithm = MessageDigest.getInstance("MD5");
algorithm.reset();
algorithm.update(md5Str.getBytes("utf-8"));
result = toHexString(algorithm.digest()).substring(8, 24);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
private static String toHexString(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (int b : bytes) {
if (b < 0) {
b += 256;
}
if (b < 16) {
hexString.append("0");
}
hexString.append(Integer.toHexString(b));
}
return hexString.toString();
}
private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "a", "b", "c", "d", "e", "f"};
/**
* 转换字节数组为16进制字串
* @param b 字节数组
* @return 16进制字串
*/
public static String byteArrayToHexString(byte[] b) {
StringBuilder resultSb = new StringBuilder();
for (byte aB : b) {
resultSb.append(byteToHexString(aB));
}
return resultSb.toString();
}
/**
* 转换byte到16进制
* @param b 要转换的byte
* @return 16进制格式
*/
private static String byteToHexString(byte b) {
int n = b;
if (n < 0) {
n = 256 + n;
}
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
/**
* MD5编码
* @param origin 原始字符串
* @return 经过MD5加密之后的结果
*/
public static String MD5Encode(String origin) {
String resultString = null;
try {
resultString = origin;
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(resultString.getBytes("UTF-8"));
resultString = byteArrayToHexString(md.digest());
} catch (Exception e) {
e.printStackTrace();
}
return resultString;
}
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;
}
}
public class OrderInfoUtil2_0 {
/**
* 要求外部订单号必须唯一。
* @return
*/
public static String getOutTradeNo() {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault());
Date date = new Date();
String key = format.format(date);
Random r = new Random();
key = key + r.nextInt();
// key = key.substring(0, 15);
return key.trim();
}
}
public class Util {
/**
** 将一个字符串转化为输入流
*
*/
public static InputStream getStringStream(String sInputString) {
if (StringUtils.StringIsNull(sInputString)) {
try {
ByteArrayInputStream tInputStringStream = new ByteArrayInputStream(sInputString.getBytes());
return tInputStringStream;
} catch (Exception ex) {
ex.printStackTrace();
}
}
return null;
}
}
public class WXHttpUtil {
public static String doPostString(String xml,String requestUrl)
{
String result = "";
try {
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod("POST");
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
// 当outputStr不为null时向输出流写数据
if (null != xml) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(xml.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
result = buffer.toString();
} catch (ConnectException ce) {
result = "";
LogUtils.Companion.e("WXHttpUtil","连接超时:{}"+ ce);
} catch (Exception e) {
result = "";
LogUtils.Companion.e("WXHttpUtil","https请求异常:{}"+ e);
}
return result;
}
//请求方法
public static String httpsRequest(String requestUrl, String requestMethod, String outputStr) {
try {
URL url = new URL(requestUrl);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
StringBuffer buffer = new StringBuffer();
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
// 释放资源
bufferedReader.close();
inputStreamReader.close();
inputStream.close();
inputStream = null;
conn.disconnect();
return buffer.toString();
} catch (ConnectException ce) {
System.out.println("连接超时:{}"+ ce);
} catch (Exception e) {
System.out.println("https请求异常:{}"+ e);
}
return null;
}
//退款的请求方法
// public static String httpsRequest2(String requestUrl, String requestMethod, String outputStr) throws Exception {
// KeyStore keyStore = KeyStore.getInstance("PKCS12");
// StringBuilder res = new StringBuilder("");
// FileInputStream instream = new FileInputStream(new File("/home/apiclient_cert.p12"));
// try {
// keyStore.load(instream, "".toCharArray());
// } finally {
// instream.close();
// }
//
// // Trust own CA and all self-signed certs
// SSLContext sslcontext = SSLContexts.custom()
// .loadKeyMaterial(keyStore, "1313329201".toCharArray())
// .build();
// // Allow TLSv1 protocol only
// SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
// sslcontext,
// new String[] { "TLSv1" },
// null,
// SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
// CloseableHttpClient httpclient = HttpClients.custom()
// .setSSLSocketFactory(sslsf)
// .build();
// try {
//
// HttpPost httpost = new HttpPost("https://api.mch.weixin.qq.com/secapi/pay/refund");
// httpost.addHeader("Connection", "keep-alive");
// httpost.addHeader("Accept", "*/*");
// httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
// httpost.addHeader("Host", "api.mch.weixin.qq.com");
// httpost.addHeader("X-Requested-With", "XMLHttpRequest");
// httpost.addHeader("Cache-Control", "max-age=0");
// httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
// StringEntity entity2 = new StringEntity(outputStr ,Consts.UTF_8);
// httpost.setEntity(entity2);
// System.out.println("executing request" + httpost.getRequestLine());
//
// CloseableHttpResponse response = httpclient.execute(httpost);
//
// try {
// HttpEntity entity = response.getEntity();
//
// System.out.println("----------------------------------------");
// System.out.println(response.getStatusLine());
// if (entity != null) {
// System.out.println("Response content length: " + entity.getContentLength());
// BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(entity.getContent()));
// String text;res.append(text);
// while ((text = bufferedReader.readLine()) != null) {
// res.append(text);
// System.out.println(text);
// }
//
// }
// EntityUtils.consume(entity);
// } finally {
// response.close();
// }
// } finally {
// httpclient.close();
// }
// return res.toString();
//
// }
// public static String getChildrenText(List children) {
// StringBuffer sb = new StringBuffer();
// if(!children.isEmpty()) {
// Iterator it = children.iterator();
// while(it.hasNext()) {
// Element e = (Element) it.next();
// String name = e.getName();
// String value = e.getTextNormalize();
// List list = e.getChildren();
// sb.append("<" + name + ">");
// if(!list.isEmpty()) {
// sb.append(getChildrenText(list));
// }
// sb.append(value);
// sb.append("</" + name + ">");
// }
// }
//
// return sb.toString();
// }
}
public class WxPaySignatureUtils {
/**
* 参数进行XML化
* @param map,sign
* @return
*/
public static String parseString2Xml(Map<String, String> map,String sign){
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
Set es = map.entrySet();
Iterator iterator = es.iterator();
while(iterator.hasNext()){
Map.Entry entry = (Map.Entry)iterator.next();
String k = (String)entry.getKey();
String v = (String)entry.getValue();
sb.append("<"+k+">"+v+"</"+k+">");
}
sb.append("<sign>"+sign+"</sign>");
sb.append("</xml>");
return sb.toString();
}
/**
* 获取签名 md5加密(微信支付必须用MD5加密)
* 获取支付签名
* @param
* @return
*/
@SuppressWarnings("rawtypes")
public static String getSign(Map<String, String> map,String key){
String sign = null;
Set<String> keySet = map.keySet();
String[] str = new String[map.size()];
StringBuilder tmp = new StringBuilder();
// 进行字典排序
str = keySet.toArray(str);
Arrays.sort(str);
for (int i = 0; i < str.length; i++) {
String t = str[i] + "=" + map.get(str[i]) + "&";
tmp.append(t);
}
if (null != key) {
tmp.append("key=" + key);
}
return MD5Util.MD5Encode(tmp.toString()).toUpperCase();
}
public static Map<String,Object> getMapFromXML(String xmlString) throws ParserConfigurationException, IOException, SAXException {
//这里用Dom的方式解析回包的最主要目的是防止API新增回包字段
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputStream is = Util.getStringStream(xmlString);
Document document = builder.parse(is);
//获取到document里面的全部结点
NodeList allNodes = document.getFirstChild().getChildNodes();
Node node;
Map<String, Object> map = new HashMap<String, Object>();
int i=0;
while (i < allNodes.getLength()) {
node = allNodes.item(i);
if(node instanceof Element){
map.put(node.getNodeName(),node.getTextContent());
}
i++;
}
return map;
}
/**
* 获取一定长度的随机字符串
* @param length 指定字符串长度
* @return 一定长度的字符串
*/
public static String getRandomStringByLength(int length) {
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
}
/**
* 获取当前时间 yyyyMMddHHmmss
* @return String
*/
public static String getCurrTime() {
Date now = new Date();
SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
String s = outFormat.format(now);
return s;
}
}
记得在build.gradle中添加一个依赖:
useLibrary 'org.apache.http.legacy'
buildTypes {
...
}
然后可能会遇到一些异常,比如调起支付页面后总是提示支付异常resp.errCode==-1,让你清理微信缓存,但无论你怎么清理偶读不行,可以试下重启手机,我好几次和微信相关的操作都是这么解决的,比如微信分享,登陆,还有支付,这个比清理微信的缓存更彻底。
微信官方文档对于errorcode=-1的原因描述如下:
1.签名错误需要注意你的应用打包时是release版本(切记key文件一定是对你这个项目是有效的,如果应用包名没变,但应用名字变了,key文件需要重新生成一个,不能用之前的了),安装到手机上,然后用微信的md5签名工具填入你应用的包名生成签名字符串复制出来,配置到微信服务器的后台参数里面。
下面有个截图也是和签名有关的,可以参考:
微信支付errorcode=-1除了项目签名的发布版本不对还有一种原因,就是服务端给客户端的参数进行二次签名md5加密的时候如果其中某个参数名不对,也会导致签名不对无法通过验证成功调起微信支付,有遇到过这样的情况,服务器二次签名的时候map里面商户id的参数名不对,服务器的商户id叫much_id,二次签名的时候不能再加这个了,Android用的商户id参数名叫partnerid,服务器做的二次签名,Android手机端直接拿到参数来调起本地微信支付的时候所以就会导致_1点错误,坑人的是微信不给一点错误的提示,什么错误都是-1,errormessage都是null,坑死人不偿命简直,都是程序员,本是同根生,相煎何太急呢!
2.未注册appid
调用手机端支付请求代码之前,用下面代码注册appid到微信中
api = WXAPIFactory.createWXAPI(this, Constants.APP_ID, false)
api?.registerApp(Constants.APP_ID)
3.项目appid和微信支付后台配置的不一样,检查下就可以了