android之那些年在微支付上踩过的坑!

本文详细介绍了微信支付APP支付的实现过程,包括获取jar包、注册APPID、下单、调起支付等步骤,并分享了作者在实践中遇到的问题及解决办法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近软件的微支付突然不能用了,经紧急查找发现微支付的支付接口更换了(心中顿时千万只xx奔腾而过,微信太霸道了,更换了也不通知一声!!)于是就开始了我的微支付踩坑之路,更新微支付花了2天的时间,虽说不长但遇到了很多问题。作为一个有情怀的程序员,把这些坑记录一下,希望能帮助到大家!

1 简单介绍

新微支付和以前的变化还是很大的,上传数据和返回数据都采用了xml的方式。这里给出微支付的官方文档地址,方便大家查阅。微支付这个官方文档弄的真心不好,完全不是给初学的的看的!!我也是在同事的帮助下才看明白。

2 添加流程

我先说一下微支付的添加流程,大家心里有个数:

第一步 获取jar包

官网下载一个demo,取出它里面的名字叫:libammsdk.jar的包。(注意:这个demo大家千万别废时间去想办法运行起来,它根本没用!)如下图:
这里写图片描述

第二步 注册APPID

// 通过WXAPIFactory工厂,获取IWXAPI的实例
IWXAPI api = WXAPIFactory.createWXAPI(this, Constants.APP_ID, false);
api.registerApp(Constants.APP_ID);

很多人调不起微支付,很多情况是忽略了这里。Constants.APP_ID要换成你们自己的app_id.

第三步 下单

这是最难的地方,我花时间最多的地方,踩过很多坑。先看一下它的官方地址

总的思路是:
第一步.组合请求参数成xml字符串。
这里面有些东西大家一定要注意:
1.nonce_str的数据一定要和sign加密的nonce_str数据是一致的!
这里给出我的nonce_str生成算法:

 public static String getNonceStr() {
//      Random random = new Random();
//      return MD5.getMessageDigest(String.valueOf(random.nextInt(10000))
//              .getBytes());
        final String[] Strarray = {"0","1","2","3","4","5","6","7","8","9",
                "A","B","C","D","E","F","G","H","I","J",
                "K","L","M","N","O","P","Q","R","S","T",
                "U","V","W","X","Y","Z"};
        StringBuffer nonceStr = new StringBuffer();
        for(int i=0;i<15;i++){
            nonceStr.append(Strarray[(int)(Math.random()*Strarray.length)]);
        }
        return nonceStr.toString();
    }

2.sign签名的时候,要注意:

    1.参数名一定要按照ASCII码从小到大排序!!
    2.sign参数不参与签名.其他的都要参与签名!
    3.签名的时候要严格按照人家的来,别弄错了!我封装了一个工具供大家使用。
//封装的签名工具
public static String getSign(String stringA){
    String strDigest = stringA+"&key="+"";
    return MD5.getMessageDigest(strDigest.getBytes()).toUpperCase();
}

使用方法 :

StringBuffer sb = new StringBuffer();
        sb.append("appid=");
        sb.append(Constants.APP_ID);
        sb.append("&body=");
        sb.append(subject);
        sb.append("&mch_id=");
        sb.append(Constants.PARTNER_ID);
        sb.append("&nonce_str=");
        sb.append(nonceStr);
        sb.append("&notify_url=");
        sb.append(weixinNortifyUrl);
        sb.append("&out_trade_no=");
        sb.append(PayToActivityUtil.getWxPaysn(pay_sn));
        sb.append("&spbill_create_ip=");
        sb.append("172.16.1.99");
        sb.append("&total_fee=");
        sb.append(String.valueOf(ipri));
        sb.append("&trade_type=");
        sb.append("APP");

    String sign = WXApiUtil.getSign(sb.toString();

第二步:发送组合好的xml字符串发送到接口地址。
接口地址人家已经给出来了,如下图:
这里写图片描述
发送的方法,我也已经封装好了,这里给大家发出代码:


/**
 * 专门为微信支付封装的一个asynctask
 */

public class WXOpAsyncTask extends AsyncTask<Void, Void, HashMap<String,String>> {
   public static final int WXOpAsyncTaskFlag1 = 0x010000;
   public static final int WXOpAsyncTaskFlag2 = 0x010001;
   public static final int WXOpAsyncTaskFlag3 = 0x010002;
    private Context  context;
    private String fuction;
    private ProgressDialog  dialog;
    private String url;
    private String entity ;
    private int wXOpAsyncTaskFlag = WXOpAsyncTask.WXOpAsyncTaskFlag1;
    public  interface  WXOpAsyncCallBack{
      public void   onPostExecute(HashMap<String, String> strHashMap,int flag);
    }
    private WXOpAsyncCallBack wXOpAsyncCallBack;
    public WXOpAsyncTask(Context  context){
        this.context = context;
    }

    public WXOpAsyncTask setFuction(String fuction) {
        this.fuction = fuction;
        return this;
    }

    public WXOpAsyncTask setwXOpAsyncTaskFlag(int wXOpAsyncTaskFlag) {
        this.wXOpAsyncTaskFlag = wXOpAsyncTaskFlag;
        return this;
    }

    public WXOpAsyncTask setUrl(String url) {
        this.url = url;
        return this;
    }

    public WXOpAsyncTask setEntity(String entity) {
        this.entity = entity;
        return this;
    }

    public WXOpAsyncTask setwXOpAsyncCallBack(WXOpAsyncCallBack wXOpAsyncCallBack) {
        this.wXOpAsyncCallBack = wXOpAsyncCallBack;
        return this;
    }

    @Override
    protected void onPostExecute(HashMap<String, String> strHashMap) {
        super.onPostExecute(strHashMap);
        if (dialog != null) {
            dialog.dismiss();
        }
        this.wXOpAsyncCallBack.onPostExecute(strHashMap,wXOpAsyncTaskFlag);
    }

    @Override
    protected void onPreExecute() {
          dialog = ProgressDialog.show(context,  "提示", fuction);
    }
    @Override
    protected HashMap<String, String> doInBackground(Void... params) {
        byte[] buf = Util.httpXmlPost(url, entity);//这里是请求
        return WChatXmlUtil.getInstance().getXmlData(new String(buf));//这里是解析
    }
    @Override
    protected void onCancelled() {
        super.onCancelled();
    }

}

使用方法:

new WXOpAsyncTask(SubmitOrderDialog.this)
                .setUrl(UrlManager.WXPlaceOrderUrl)
                .setEntity(genProductArgs())//传入组合的xml参数
                .setFuction("正在下单...")//加载提示语
                .setwXOpAsyncCallBack(this)//设置回调,这里写一个this,所以acitivty需要实现WXOpAsyncTask.WXOpAsyncCallBack接口。
                .setwXOpAsyncTaskFlag(WXOpAsyncTask.WXOpAsyncTaskFlag1)
                .execute();
/*
    回调
*/
    @Override
    public void onPostExecute(HashMap<String, String> strHashMap,int flag) {
        switch (flag){
            case WXOpAsyncTask.WXOpAsyncTaskFlag1:
                if (strHashMap == null || NormalUtil.isEmpty(strHashMap.get("prepay_id"))) {
                    Toast.makeText(SubmitOrderDialog.this,"支付失败!",Toast.LENGTH_SHORT).show();
                    return ;
                }
                sendPayReq(strHashMap);
                break;
        }
    }

下面就是其中用到的其他方法:

public static byte[] httpXmlPost(String urlStr, String entity) {
        if (NormalUtil.isEmpty(entity)) {
            DebugLogUtil.getInstance().Error("httpPost, url is null");
            return null;
        }
        try {
            URL url = new URL(urlStr);
            byte[] xmlbyte = entity.toString().getBytes("UTF-8");
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setDoOutput(true);// 允许输出
            conn.setDoInput(true);
            conn.setUseCaches(false);// 不使用缓存
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Connection", "Keep-Alive");// 维持长连接
            conn.setRequestProperty("Charset", "UTF-8");
            conn.setRequestProperty("Content-Length",
                    String.valueOf(xmlbyte.length));
            conn.setRequestProperty("Content-Type", "text/xml; charset=UTF-8");
            conn.setRequestProperty("X-ClientType", "2");//发送自定义的头信息

            conn.getOutputStream().write(xmlbyte);
            conn.getOutputStream().flush();
            conn.getOutputStream().close();


            if (conn.getResponseCode() != 200)
                throw new RuntimeException("请求url失败");

            InputStream is = conn.getInputStream();// 获取返回数据
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int len;
            while ((len = is.read(buf)) != -1) {
                out.write(buf, 0, len);
            }
            String string = out.toString("UTF-8");
            return out.toByteArray();
        } catch (Exception e) {
            DebugLogUtil.getInstance().Error("httpPost exception, e = " + e.getMessage());
            e.printStackTrace();
            return null;
        }
    }
/**
 * Created by YuYuanDa on 2017-03-29.
 * Administrator yyd
 * 注意:本次封装只是针对微信支付封装的,支持简单格式xml.
 * 作用:生成xml;
 */

public class WChatXmlUtil {
    private StringWriter writer;
    private XmlSerializer serializer;
    private WChatXmlUtil(){
        writer = new StringWriter();
        serializer = Xml.newSerializer();
        writer = new StringWriter();
    }

    /**
     * 不是单例模式,此封装只是为了写代码方便一些。
     * @return
     */
    public static WChatXmlUtil getInstance(){
        return new WChatXmlUtil();
    }
    public WChatXmlUtil start(){
        try {
            serializer.setOutput(writer);
            serializer.startTag(null,"xml");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return this;
    }

    public WChatXmlUtil addTag(String tag, String data){
        try {
            serializer.startTag(null,tag);
            serializer.text(data);
            serializer.endTag(null,tag);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return this;
    }
    public WChatXmlUtil end(){
        try {
            serializer.endTag(null,"xml");
            serializer.endDocument();
            serializer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return this;
    }
    public String getXmlByStr(){
        return writer.toString();

    }

    /**
     * 解析xml字符串
     * @param data
     * @return
     * 请注意,这里会亦错误,请在博客最底下,下载jar包!!!!
     */
    public HashMap<String,String> getXmlData(String data){
        HashMap<String,String> map = new HashMap<String,String>();
        Document doc = null;
        try {
            doc = DocumentHelper.parseText(data); // 将字符串转为XML
            Element rootElt = doc.getRootElement(); // 获取根节点
            List<Element> list = rootElt.elements();//获取根节点下所有节点
            for (Element element : list) {  //遍历节点
                map.put(element.getName(), element.getText()); //节点的name为map的key,text为map的value
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }
}

组合xml参数的代码如下:

String sendReqStr = WChatXmlUtil.getInstance()
                .start()
                .addTag("mch_id",Constants.PARTNER_ID)
                .addTag("trade_type","APP")
                .addTag("sign", WXApiUtil.getSign(sb.toString()))//不参与签名
                .addTag("nonce_str",nonceStr)
                .addTag("total_fee",String.valueOf(ipri))
                .addTag("spbill_create_ip","172.16.1.99")
                .addTag("notify_url",weixinNortifyUrl)
                .addTag("body",subject)
                .addTag("appid",Constants.APP_ID)
                .addTag("out_trade_no",PayToActivityUtil.getWxPaysn(pay_sn))
                .end()
                .getXmlByStr();

代码确实有点乱哈,大家自己整理一下。
第三步:解析返回的数据,获取prepay_id。
微支付返回的东西不太符合我们的要求所以得解析一下,我已经在异步中解析成HashMap了,在回调方法onPostExecute中我们直接拿来用就行。然后在回调方法中调起支付

第四步 调起支付

调起支付不需要我们自己请求网络了,用微支付给我们提供的方法即可,但是里面还有一个签名,签名的方法和上面是一样的,只是签名的字段不一样而已。这个地方大家要仔细,别再弄错了。
String nonceStr = WXApiUtil.getNonceStr();
PayReq request = new PayReq();
request.appId = Constants.APP_ID;
request.partnerId = Constants.PARTNER_ID;
request.prepayId= strHashMap.get(“prepay_id”);
request.packageValue = “Sign=WXPay”;
request.nonceStr= nonceStr;
request.timeStamp = “”+ (System.currentTimeMillis()/1000);
request.sign= WXApiUtil.getSign(sb.toString());
api.sendReq(request);

第五步 回调结果

不管你调支付是否成功,微支付都会回调WXPayEntryActivity中的onResp()方法,代码如下:

@Override
    public void onResp(BaseResp resp) {

        if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {

            if(resp.errCode==BaseResp.ErrCode.ERR_OK){
            Toast.makeText(this, "支付成功",Toast.LENGTH_SHORT).show();
            }else{

                Toast.makeText(this, "支付失败",Toast.LENGTH_SHORT).show();
            }

        }else{

//          Toast.makeText(this, "支付失败",Toast.LENGTH_SHORT).show();
        }

    }

第六步 结束

好了到此为止,相信你已给调起支付了。

3 注意

有一点需要注意,xml解析需要用到一个dom4j_1.6.1.jar包,点击这里下载。把这个解压后,只要里面的dom4j_1.6.1.jar文件就行。

4 结尾

好了就讲到这里吧,我封装了不少代码,代码贴的有点多,有点乱,如果有不懂的地方请留言告诉我。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序编织梦想

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值