微信支付(微信支付的坑基本上都踩过一遍了)

原创 2016年09月06日 15:27:16


项目中要用到支付宝支付和微信支付,先做了支付宝,本以为微信也很简单,看过文档之后又亲测了一遍,踩了好几个坑。。。顺便吐槽下微信的开发文档再见

开始项目:

准备工作:

1、微信开放平台:https://open.weixin.qq.com/,注册账号,获取appid和密钥。
2、大家也可以下载下官方的demo。我当时下载的例子里的统一下单接口都是错的。
3、创建应用,此处注意填写应用的包名,和应用签名,应用签名可以用签名工具生成。签名工具在平台上都可以下得到。
4、微信支付是需要申请的,成功之后会有个商户id。

接口链接

URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder

统一下单文档大家可以看看。

3、导入微信sdk

调用App支付

在研究过官方的demo之后,发现他只能获取到accessToken,但是到了获取prepayid就开始报错:打印了就会发现是:现在有错误返回{"errcode":268497023,"errmsg":"您已完成交易接口升级,老接口交易权限已关闭,请使用新接口进行交易。如有疑问请联系微信支付客服咨询"}--(很明显接口都没更新成最新的!!!不是最新的,不知道后来跟新了没有),为了保证安全的情况下,我们要从后台获取repayid。但是我们后台并没有写,所以,我下边讲的代码都是在客户端生成prepayid。

首先,需要你在开放平台上获取到的app_ID、密钥、商户账号


加上请求的参数通过统一下单的接口来获取会话标识,在return_code 和result_code都为SUCCESS的时候获取prepay_id(prepay_id我在客户端生成,有效时期24小时,用于后边吊起支付界面。)在这一步中我们还要生成签名并把它当作参数之一提交,许多时候都是签名的错误而难以生存prepay_id。下面的参数就是用来获取会话标识的。

private String genProductArgs() {
		StringBuffer xml = new StringBuffer();
		try {
			String nonceStr = genNonceStr();
			xml.append("");
            List packageParams = new LinkedList();
			packageParams.add(new BasicNameValuePair("appid", Constants.APP_ID));	
			packageParams.add(new BasicNameValuePair("body", body));
			packageParams.add(new BasicNameValuePair("mch_id", Constants.MCH_ID));
			packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
			packageParams.add(new BasicNameValuePair("notify_url", notify_url));
			packageParams.add(new BasicNameValuePair("out_trade_no", outTradNo));
			packageParams.add(new BasicNameValuePair("spbill_create_ip","127.0.0.1"));
			packageParams.add(new BasicNameValuePair("total_fee", total_fee));
			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.toString().getBytes(), "ISO8859-1");
			return xmlstring;
		} catch (Exception e) {
			Log.v(TAG, "genProductArgs fail, ex = " + e.getMessage());
			return null;
		}

	}
	
	/**
	 * 生成签名
	 */
	private String genPackageSign(List 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.API_KEY);
		String packageSign = MD5.getMessageDigest(sb.toString().getBytes())
				.toUpperCase(Locale.CHINA);
		Log.e("签名-->", packageSign);
		return packageSign;
	}
	
	

通过以上代码就完成了第一步,我们得到了prepay_id,现在就要吊起支付的界面,根据文档上给出的需要请求的参数,



empty
	private void genPayReq() {

		req.appId = Constants.APP_ID;
		req.partnerId = Constants.MCH_ID;
		req.prepayId = resultunifiedorder.get("prepay_id");
		req.packageValue = "Sign=WXPay";
		req.nonceStr = genNonceStr();
		req.timeStamp = String.valueOf(genTimeStamp());

		List signParams = new LinkedList();
		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.v("orion-signParams-->", signParams.toString());
		
		sendPayReq();
		

	}

到这里支付就完成了一大半了,现在要处理的就是支付成功、失败之后的情况,和取消支付的处理。结果处理:

要在WXPayEntryActivity里处理!而这个文件就更神奇了,一定要放在指定的包下,你的包名.wxapi,你的包名.wxapi,你的包名.wxapi,重要的事情说三遍,不要问我为什么,微信就是这么搞回调。

public void onResp(BaseResp resp) {




if(resp.errCode==-1){
setJieguowei(resp.errCode);
Toast.makeText(WXPayEntryActivity.this, "支付错误", Toast.LENGTH_SHORT).show();
WXPayEntryActivity.this.finish();
}
if(resp.errCode==0){

//后续处理
Toast.makeText(WXPayEntryActivity.this, "支付成功", Toast.LENGTH_SHORT).show();
WXPayEntryActivity.this.finish();
}
if(resp.errCode==-2){
Toast.makeText(WXPayEntryActivity.this, "用户取消", Toast.LENGTH_SHORT).show();
WXPayEntryActivity.this.finish();
}


}

下面是微信支付的整个工具类,网络请求的封装和MD5的加密工具都没贴出,我会发在下载的文件夹里。

emptypublic class WxPayUtile {
	private Context context;
	PayReq req;
	final IWXAPI msgApi;
	Map resultunifiedorder;
	StringBuffer sb;
	private String total_fee;
	private String notify_url;
	private String body;
	private String outTradNo;

	private static final String TAG = "123";


	/**
	 *
	 *   用法:WxPayUtile wxPayUtile = new WxPayUtile(getActivity(),"1","服务器回调地址","产品名称","订单号");
	           wxPayUtile.doPay();
	 *
	 * @param context   上下文
	 * @param total_fee   价格
	 * @param notify_url   服务器回调地址
	 * @param body   商品名称或其他
	 * @param outTradNo  定单号
	 */
	public WxPayUtile(Context context, String total_fee, String notify_url,
			String body, String outTradNo) {
		super();
		msgApi = WXAPIFactory.createWXAPI(context, null);
		req = new PayReq();
		msgApi.registerApp(Constants.APP_ID);
		sb = new StringBuffer();
		this.context = context;
		this.total_fee = total_fee;
		this.notify_url = notify_url;
		this.body = body;
		this.outTradNo = outTradNo;
	}

	public static WxPayUtile getInstance(Context context, String total_fee,
			String notify_url, String body, String outTradNo) {

		return new WxPayUtile(context, total_fee, notify_url, body, outTradNo);
	}

	public void doPay() {

		GetPrepayIdTask getPrepayId = new GetPrepayIdTask();
		getPrepayId.execute();
	}

	/**
	 * 生成签名
	 */
	private String genPackageSign(List 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.API_KEY);
		String packageSign = MD5.getMessageDigest(sb.toString().getBytes())
				.toUpperCase(Locale.CHINA);
		Log.e("签名-->", packageSign);
		return packageSign;
	}

	private String genAppSign(List 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.API_KEY);

		this.sb.append("sign str\n" + sb.toString() + "\n\n");
		String appSign = MD5.getMessageDigest(sb.toString().getBytes())
				.toUpperCase();
		Log.e("app签名-->", appSign);
		return appSign;
	}

	private String toXml(List params) {
		StringBuilder sb = new StringBuilder();
		sb.append("");
		for (int i = 0; i < params.size(); i++) {
			sb.append("<" + params.get(i).getName() + ">");

			sb.append(params.get(i).getValue());
			sb.append("");
		}
		sb.append("");

		Log.e("orion-sb--->", sb.toString());
		return sb.toString();
	}

	private class GetPrepayIdTask extends
			AsyncTask> {


		@Override
		protected void onPreExecute() {

			Toast.makeText(context, "正在准备支付", Toast.LENGTH_SHORT).show();
		}

		@Override
		protected void onPostExecute(Map result) {

			sb.append("prepay_id\n" + result.get("prepay_id") + "\n\n");
			resultunifiedorder = result;

//			Log.e("orion-result_code-->", result.get("result_code"));
			if (result.get("result_code").equals("SUCCESS")) {
				Log.v("orion-result_code-->", "IS SUCCESS!");
//				Toast.makeText(context, "成功支付123654", Toast.LENGTH_SHORT).show();
				genPayReq();
			}
			
				if (result.get("result_code").equals("FAIL")) {
					String miaoshuString=result.get("err_code_des");
					Toast.makeText(context, miaoshuString, Toast.LENGTH_SHORT).show();	
				}
				

		}

		@Override
		protected void onCancelled() {
			super.onCancelled();
			Toast.makeText(context, "quxiao", Toast.LENGTH_SHORT).show();	
		}

		@Override
		protected Map doInBackground(Void... params) {

			String url = String
					.format("https://api.mch.weixin.qq.com/pay/unifiedorder");
			String entity = genProductArgs();

			Log.v("entity-->", entity);

			byte[] buf = HttpWxUtile.httpPost(url, entity);

			String content = new String(buf);
			Log.v("content-->", content);
			Map xml = decodeXml(content);

			return xml;
		}
	}

	public Map decodeXml(String content) {

		try {
			Map xml = new HashMap();
			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.v("orion-e--->", 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 genProductArgs() {
		StringBuffer xml = new StringBuffer();
		try {
			String nonceStr = genNonceStr();
			xml.append("");
            List packageParams = new LinkedList();
			packageParams.add(new BasicNameValuePair("appid", Constants.APP_ID));	
			packageParams.add(new BasicNameValuePair("body", body));
			packageParams.add(new BasicNameValuePair("mch_id", Constants.MCH_ID));
			packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
			packageParams.add(new BasicNameValuePair("notify_url", notify_url));
			packageParams.add(new BasicNameValuePair("out_trade_no", outTradNo));
			packageParams.add(new BasicNameValuePair("spbill_create_ip","127.0.0.1"));
			packageParams.add(new BasicNameValuePair("total_fee", total_fee));
			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.toString().getBytes(), "ISO8859-1");
			return xmlstring;
		} catch (Exception e) {
			Log.v(TAG, "genProductArgs 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 = "Sign=WXPay";
		req.nonceStr = genNonceStr();
		req.timeStamp = String.valueOf(genTimeStamp());

		List signParams = new LinkedList();
		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.v("orion-signParams-->", signParams.toString());
		
		sendPayReq();
		

	}

	private void sendPayReq() {

		msgApi.registerApp(Constants.APP_ID);
			
		msgApi.sendReq(req);
//		msgApi.handleIntent(arg0, arg1)
		
		
	}


}

签名错误:解决办法

1、我们要在打包的状态下测试。
2、检查包名是否正确,平台上的包名和应用的包名是否一致
3、打包之后再用签名工具生成一次签名,(我就是出了这个鬼。同样的签名文件打包两次的签名竟然不一样,当然后面就都一样了)

工具下载地址:http://download.csdn.net/detail/heiya0409/9642251









版权声明:本文为博主原创文章,未经博主允许不得转载。

访问微信h5支付中的mweb_url支付跳转链接,一片空白

h5支付是微信2107年下半年推出来的一种支付方式, 可以让你的h5网站在移动设备上调用微信支付接口, 实现用户在线支付功能。 一、 获得mweb_url支付跳转链接 在进行微信h5支...
  • towtotow
  • towtotow
  • 2018年01月06日 11:35
  • 573

微信支付趟过的坑

这段时间在做微信支付开发,在公司的公众号审批下来后,我这边的测试用例也已经开发完毕,于是拿着具体的数据来调试了,大段大段的代码就不贴了,demo里有,这里就说说调试过程中遇到的坑。...
  • molaifeng
  • molaifeng
  • 2015年01月21日 15:49
  • 75783

微信支付页面不显示以及空白页error -1

一:微信支付页面不显示 : 1,微信支付的时候只能是签名包才能调用,手机要有微信才能用,也就是说测试微信支付的时候你要打包apk在测试,把微信应用平台的应用签名改成打包之后的签名 2,检查文件WXPa...
  • ludamila
  • ludamila
  • 2016年08月19日 10:26
  • 6539

钱柜娱乐开户微信支付问题总结

钱柜娱乐开户快速实现微信支付 onPayFinish, errCode = -1  当你参数签名都没有问题的时候,出现这个提示,请按照如下操作: 在你的项目测试微信的组件(分享、支付等)的时候,...
  • jdsjlzx
  • jdsjlzx
  • 2015年08月11日 16:26
  • 69097

微信支付服务端的一些坑及最终解决

有1年多没搞微信支付了,最近跳槽,要重做APP,又来接触微信这个坑比。OK,不多说,上代码。以下是我的一个controller类,重点在下面  // 微信交易类型       private st...
  • zzxl360362656
  • zzxl360362656
  • 2017年07月31日 14:31
  • 361

微信支付详解与取消支付回调

第一步:绑定域名 先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。如果你使用了支付类接口,请确保支付目录在该安全域名下,否则将无法完成支付。 备注:登录后可在“开发者中心...
  • ziwoods
  • ziwoods
  • 2017年02月22日 15:46
  • 3072

微信支付详解与取消支付回调

第一步:绑定域名 先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。如果你使用了支付类接口,请确保支付目录在该安全域名下,否则将无法完成支付。 备注:登录后可在“开发者中心...
  • ziwoods
  • ziwoods
  • 2017年02月22日 15:46
  • 3072

谈谈那些年微信支付踩过的坑

很早的时候就想写这篇文章了,作为BAT中的一员,还真不想吐槽它,免得被人身攻击。有人说,微信支付很简单嘛,官网有例子,网上也有现成的例子,不过谁用谁知道,本人也是在深入了解之后,真心觉得微信支付里的坑...
  • Baple
  • Baple
  • 2017年06月02日 09:44
  • 1079

微信支付遇到的坑

1.首先,根据开发文档,申请APP_ID(至于怎么获得签名,这个下个签名工具就可以了) 获得了APP_ID之后,支付和分享都需要用到。此时分享就可以直接使用了。但是支付还需要申请。(如果此时微信分享不...
  • u012975370
  • u012975370
  • 2016年03月24日 14:13
  • 1986

微信支付服务端开发总结

前言 最近应公司业务需求,把微信支付完成了,当然已经顺利上线。但是开发的过程是也是踩了很多坑,下面我就先说说开发流程,以及在开发中遇到的大大小小的坑。 开发流程 首先,看一下微信...
  • Baple
  • Baple
  • 2017年03月29日 09:09
  • 2052
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:微信支付(微信支付的坑基本上都踩过一遍了)
举报原因:
原因补充:

(最多只允许输入30个字)