Android集成微信支付跳坑指南

1. 前言

“好记性不如烂笔头”,现在 平常 碰到的一些知识点都是直接复制到印象笔记中,但是那些资料都比较零散,该总结的还得总结。前段时间完成了在线支付的微信支付,趁着一些注意地方没忘记,这里记录下来吧!

2. 准备

这里我不想吐槽,微信支付 比 支付宝 支付准备工作 麻烦不知道多少倍,我记忆中有三个地方的审核,当然这些不是开发内容,可能随时会变,开发时具体是什么就是什么,我这里只是给大家提个醒。

  • 企业认证,资料填写,等待审核

  • 应用创建认证,这里填写 Android 和 Ios 两边项目的一些信息,苹果不用说了,主要 是 安卓,需要填写签名,很多人或者资料都建议先填写 测试的签名 ,为了开发平常开发测试使用方便,但是我建议使用正式的签名,有很多办法解决 ,因为这里涉及到 签名 的获取,还是留到后面一起说吧。

  • 支付认证审核,应用创建后,默认是没有支付服务的,需要开通支付,又要填写资料,商家收款银行卡,最主要的是需要 法人身份证 正方面,这里特别要注意,当初苦逼的和领导说 需要 老板的身份证 正方面把他都吓一跳(上市公司老板 身价比较高…)

虽然微信有各种认证,各种等待,虽然提示 7 个工作日内,但是他们的效率,简直没的说,比支付宝快,一般来说半天或者一天,审核结果就下来了,这里给微信点个赞。

2.1 应用签名

这里单独列出来,因为这里涉及到 签名 的获取和使用 测试签名 还是 正式签名
微信当然提供了一个 apk 用来获取 app的签名,只要我们将已经签名的项目APP安装到手机上,然后再安装他们的 apk 获取签名,其实我不太建议这样做,有点麻烦,而且他们 apk 签名工具太反人类了,有木有,显示出来的 一大串字符串 ,又不能复制,只能对着电脑一个一个敲,有点容易出错。
其实应用签名通过处理 SHA1 值获取

SHA1: 68:1Y:0D:89:AC:4B:B3:OF:90:64:83:UW:95:44:B7:49:36:03:AE:27

对于如上一串 SHA1 值,将 [ : ] 去掉,大写字母改成小写,这就是 应用签名,特别注意,这里使用正式 keystore ,获取 SHA1 ,获取到的就是正式应用签名,对于应用怎样获取 SHA1 ,可以看我以前总结的一篇博客

* 特别注意 不是 SHA1 值 是 MD5值 进行 大写改小写 去掉 : *

快速获取debug.keystore和release.keystore的SHA1或者MD5值


很多资料都说在微信平台先填写测试的应用签名,方便测试,然后改回正式签名上线,其实没必要,一来呢修改签名是需要审核的,最低半天时间,然后呢有其他办法方便测试。具体看我以前总结的博客,大致说下思路,要么直接在 buildTypes.debug 下配置正式签名打包,这样运行时,直接签名不是默认的 debug ,而是我们覆盖设置的 正式签名。


点击看大图

还有一种办法 直接去 AS–>Build –>Select Build Variant –> 选择 release ,然后运行 AS。
前面那种办法我亲测有用,只有后面那种应该也可以,我在以前微信第三方登录时使用过,是可以的,因该是一个道理,没问题的。具体看下面博客吧!

项目gradle学习之路(1)


3 开发
如果调起支付出问题或者调不起微信支付,一般都会返回 -1 ,这个问题比较严重,网上都出了 类似的搜索词条了,这里我也碰到过 -1 ,我只能说明下 我检查的分析和手段,首先 一定要保证 微信平台的的签名和运行的APP签名一致,这一定要再三检查下,可以使用最老的办法,通过
Build –> Generate Signed APK –> 加上签名把 APK 生成出来在安装到手机上,微信端保证处于打开状态,然后网上说有可能需要清空微信的缓存,这里我倒是没有。


如果上述都没问题,但还是报 -1 ,那可能就是 二次签名 sign 出问题了,这个 sign 获取方式,微信提供了思路,但是竟然没提供函数方法,难道想考验开发的算法功力。。。

签名生成的通用步骤如下:

第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。

特别注意以下重要规则:

◆ 参数名ASCII码从小到大排序(字典序);
◆ 如果参数的值为空不参与签名;
◆ 参数名区分大小写;
◆ 验证调用返回或微信主动通知签名时,传送的sign参数不参与签名,将生成的签名与该sign值作校验。
◆ 微信接口可能增加字段,验证签名时必须支持增加的扩展字段
第二步,在stringA最后拼接上key得到stringSignTemp字符串,并对stringSignTemp进行MD5运算,再将得到的字符串所有字符转换为大写,得到sign值signValue。

这里有个要注意的点,先看下面文档上要求发起支付请求的参数
这里写图片描述

先说下我自己认为的微信支付流程: app界面上选定金额后点击微信支付,先调用后台提供的接口,传递一些参数过去,最主要的参数当然是 金额大小,然后后台返回给我们结果,包含上图中除了 应用ID 以外的剩下参数,我们在将这些参数设置到微信提供的 API 中,向微信发起支付请求 。

sign 这个字段是后台生成返回给我们的,我们在设置到请求参数中,所以后台在根据微信提供生成 sign 的算法时,上图中的 拓展字段 package 和 我们 用微信 API 设置参数的 字段不一样,

payRequest.packageValue = "Sign=WXPay";

看到没,所以我们后台在我一直 返回 -1 时也有疑问,到底使用哪个作为拼接的 key 。其实这里使用的还是
package。当然了 我们后台确实是他给我的 sign 出问题了,导致 公众号支付 IOS 支付都没问题,就我 安卓一直不行,最后我决定自己生成 sign 来说服他。

首先获取到后台返回给我们的所有数据,把他提供的签名 sign 去掉,然后 采用微信提供的 生成 sign
思路获取到 sign,再将和 sign 一起的数据一起设置到 微信API 中,如果可以成功调用 微信支付,则 sign 确实后台返回给我们的有问题。
自己拼接 key value 然后加密,可能会出错或者慢, 这里我在网上找了个生成 sign 的算法,找不到来源了,写博客的今天和当初时间很久了。

package com.guo.firstdemo;

import org.junit.Test;

import java.util.*;

/**
 * Created by [email protected] on 2018/2/27
 * Description:
 */
public class SignDemo {

    /**
     * 获取sign签名
     *
     * @return
     */
    @Test
    public void genPayReq() {

        // 把参数的值传进去SortedMap集合里面
        SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();

        //应用ID
        parameters.put("appid", "wx4446aecbce287ab8");

        //随机字符串
        parameters.put("noncestr", "48xQcKUtC2SULcmg");

        //扩展字段
        parameters.put("package", "Sign=WXPay");

        //商户号
        parameters.put("partnerid", "1499280172");

        //预支付交易会话ID
        parameters.put("prepayid", "wx201802271751141caa31c4510450228340");

        //时间戳
        parameters.put("timestamp","1519725074");

        String characterEncoding = "UTF-8";
        String mySign = createSign(characterEncoding, parameters);
        System.out.println("我的签名是:" + mySign);
    }



    /**
     * 微信支付签名算法sign
     *
     * @param characterEncoding
     * @param parameters
     * @return
     */
    public static String createSign(String characterEncoding,
                                    SortedMap<Object, Object> parameters) {
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();// 所有参与传参的参数按照accsii排序(升序)
        Iterator it = es.iterator();
        while (it.hasNext()) {
            @SuppressWarnings("rawtypes")
            Map.Entry entry = (Map.Entry) it.next();
            String k = (String) entry.getKey();
            Object v = entry.getValue();
            if (null != v && !"".equals(v) && !"sign".equals(k)
                    && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + "a0fa2659hdf65auh345fc9f6567k57fb5575f9c4"); //KEY是商户秘钥
        System.out.println("   ---sb--   "+sb.toString());
        String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding)
                .toUpperCase();
        return sign;
    }


}
package com.guo.firstdemo;

import java.security.MessageDigest;

/**
 * Created by [email protected] on 2018/2/27
 * Description:
 */
public class MD5Util {
    private static String byteArrayToHexString(byte b[]) {
        StringBuffer resultSb = new StringBuffer();
        for (int i = 0; i < b.length; i++)
            resultSb.append(byteToHexString(b[i]));

        return resultSb.toString();
    }

    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0)
            n += 256;
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    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;
    }

    private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}

里面 //KEY是商户秘钥 有点重要 注意别泄露 。
好的 我到这里就解决了,后台修改了 生成 sign 的算法,成功调起了 微信支付。

猜你喜欢

转载自blog.csdn.net/guozhaohui628/article/details/79446264
今日推荐