Android微信支付接入

前言:本文主要说明如何在Android项目中接入微信支付,介绍微信支付在项目中的配置,分析微信支付数据与其交互流程,分享个人遇到的坑,以帮助有需要的朋友能更快的在项目中进行微信支付的接入。 
正文: 
1,开发资质申请: 
这个过程在本文中不详细介绍,总的来说需要企业资质申请微信支付功能以及微信开发者,最终是为了得到APPID和商户密钥(后面会详讲这个两个值怎么使用) 
2,流程总体介绍 
微信支付总体结构图: 
这里写图片描述 
订单信息:简单来讲就是对订单的封装,其中包含了支付金额,支付时间,订单号,签名信息等等订单信息。这些信息在项目中一般是由服务端构建的,交付给APP转发给微信服务器即可。但是实际情况中服务端可能也没有做这个工作,那么订单信息也有可能在APP端合成。所以开发时务必和后端开发人员协商好关于订单信息构造的工作。(订单构造详情见下文) 
支付请求:即APP在程序中调用支付SDK将订单信息发送给微信服务器,这个过程在代码中是很简单的,微信封装好了这个请求接口执行一行代码即可调用(详情见下文) 
支付结果反馈:微信会在特定回调接口通知支付接口 
总体上这就是微信支付的结构,操作上稍微复杂的是订单信息的构造,容易导致出错的微信SDK的配置。那么接下来就具体的一一介绍每个点的实现。 
3,下载官方demo 
官网SDK以及demo下载地址:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419319167&token=&lang=zh_CN 
官方文档地址:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=1_1 
下载完成后将libammsdk.jar导入到项目中,至于官方demo,建议大家看看就好,不要太相信。因为写的是在太烂了(不知道为什么微信这么大个平台,开发的demo是如此的不考虑用户的感受) 
4,实现 
a,导入jar包 
b,在工程包名下根目录新建wxapi包,这个wxapi包的位置一定要是包名根目录,包名也不能错。(这让对包名有强迫症的开发者会很不爽,但是没办法) 
这里写图片描述 
c,在该包下新建回调类WXPayEntryActivity,该类的名字一定是在wxapi包下,名字也一定是WXPayEntryActivity。因为他是微信回调的接口,如果改变位置或名字,就会导致微信无法将支付结果反馈,从而导致支付失败。 
这里写图片描述 
(图中WXEntryActivity是用作微信分享或其他功能的,只做微信支付的朋友可以忽略) 
d,WXPayEntryActivity的代码实现以及使用说明: 
该类是不需要开发者自己调用的,他是在微信支付后SDK为了回调通知APP的接口,SDK内部会调用该Activity,该Activity的界面布局可以自定义的设置。 
其具体的回调接口是该类中的onResp函数,在该函数中可判断是否支付成功。

public class WXPayEntryActivity extends AppCompatActivity implements IWXAPIEventHandler {

    private IWXAPI api;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wxpay_entry);
        api = WXAPIFactory.createWXAPI(this, "wxAPPID");//这里填入自己的微信APPID
        api.handleIntent(getIntent(), this);
    }

    @Override
    public void onReq(BaseReq baseReq) {

    }

    @Override
    public void onResp(BaseResp baseResp) {
        Log.d("coyc", "onPayFinish, errCode = " + baseResp.errCode);

        if (baseResp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
            int errCord = baseResp.errCode;
            if (errCord == 0) {
                App.getInstance().tos("支付成功!");
            } else {
                App.getInstance().tos("支付失败");
            }
            //这里接收到了返回的状态码可以进行相应的操作,如果不想在这个页面操作可以把状态码存在本地然后finish掉这个页面,这样就回到了你调起支付的那个页面
            //获取到你刚刚存到本地的状态码进行相应的操作就可以了
            finish();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        api.handleIntent(intent, this);
    }
}

e,AndroidManifest.xml配置:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

        <!-- 微信支付 -->
        <activity
            android:name=".wxapi.WXPayEntryActivity"
            android:exported="true"
            android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />

                <data android:scheme="自己的wxAPPID" />
            </intent-filter>
        </activity>

f,调用支付接口:

IWXAPI api= WXAPIFactory.createWXAPI(context, "wxAPPID",false);//填写自己的APPID
api.registerApp("wxAPPID");//填写自己的APPID,注册本身APP
PayReq req = new PayReq();//PayReq就是订单信息对象
//给req对象赋值
req.appId = appid;//APPID
req.partnerId = partnerid;//    商户号
req.prepayId = prepayid;//  预付款ID
req.nonceStr = getRoundString();//随机数
req.timeStamp = getTimeStamp();//时间戳
req.packageValue = "Sign=WXPay";//固定值Sign=WXPay
req.sign = sign;//签名

api.sendReq(req);//将订单信息对象发送给微信服务器,即发送支付请求

那么写到这里,上面的PayReq 对象其实是未被赋值的

        req.appId = appid;//APPID
        req.partnerId = partnerid;//    商户号
        req.prepayId = prepayid;//  预付款ID
        req.nonceStr = getRoundString();//随机数
        req.timeStamp = getTimeStamp();//时间戳
        req.packageValue = "Sign=WXPay";//固定值Sign=WXPay
        req.sign = sign;//签名

而且这7个参数是缺一不可的,如果这些参数都是在服务端生成的,那么在Android开发人员就不用做什么工作了,将服务端给的值给其赋值就好了,那么整个微信支付流程也就做完了(一般来讲也确实是这个样子)。但是有些情况下服务端的同事没有做这些工作,比如说签名工作等等。那么这么这些工作就要在APP本地实现。 
下面我给出随机数获取算法,时间戳获取算法,签名算法。

private String getRoundString() {

        Random random = new Random();

        return random.nextInt(10000) + "";
    }

    private String getTimeStamp() {
        return new Date().getTime() / 10 + "";
    }
    private String getSign() {
        Map<String, String> map = new HashMap<>();
        map.put("appid", req.appId);
        map.put("partnerid", req.partnerId);
        map.put("prepayid", req.prepayId);
        map.put("package", req.packageValue);
        map.put("noncestr", req.nonceStr);
        map.put("timestamp", req.timeStamp);

        ArrayList<String> sortList = new ArrayList<>();
        sortList.add("appid");
        sortList.add("partnerid");
        sortList.add("prepayid");
        sortList.add("package");
        sortList.add("noncestr");
        sortList.add("timestamp");
        sort(sortList);

        String md5 = "";
        int size = sortList.size();
        for (int k = 0; k < size; k++) {
            if (k == 0) {
                md5 += sortList.get(k) + "=" + map.get(sortList.get(k));
            } else {
                md5 += "&" + sortList.get(k) + "=" + map.get(sortList.get(k));
            }
        }
        String stringSignTemp = md5+"&key=商户密钥";//这里填写自己的商户密钥,所以说如果签名工作实在服务端完成的,商户密钥在APP端是用不到的

        String sign=MD5.Md5(stringSignTemp).toUpperCase();

        return sign;
    }

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.android.pc.util;

import java.security.MessageDigest;

public class MD5 {
    public MD5() {
    }

    public static final String Md5(String s) {
        char[] hexDigits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

        try {
            byte[] e = s.getBytes("UTF-8");
            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(e);
            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 & 15];
                str[k++] = hexDigits[byte0 & 15];
            }

            return new String(str);
        } catch (Exception var10) {
            return null;
        }
    }
}

商户号以及预付款ID这个必须是服务端同事给定的值,在APP端是无法代码生成的。 
那么整个微信支付的代码工作到这里就结束了。这里我将自己封装的支付类贴出来,仅供参考。

public class WX_Pay {

    public IWXAPI api;
    private PayReq req;

    public WX_Pay(Context context) {

        api = WXAPIFactory.createWXAPI(context, "wxAPPID",false);
    }

    /**
     * 向微信服务器发起的支付请求
     */
    public void pay(String appid,String partnerid,String prepayid) {

        req = new PayReq();

        req.appId = appid;//APPID
        req.partnerId = partnerid;//    商户号
        req.prepayId = prepayid;//  预付款ID
        req.nonceStr = getRoundString();//随机数
        req.timeStamp = getTimeStamp();//时间戳
        req.packageValue = "Sign=WXPay";//固定值Sign=WXPay

        String sign = getSign();
        req.sign = sign;//签名

        api.registerApp("wxAPPID");
        api.sendReq(req);
    }

    @NonNull
    private String getSign() {
        Map<String, String> map = new HashMap<>();
        map.put("appid", req.appId);
        map.put("partnerid", req.partnerId);
        map.put("prepayid", req.prepayId);
        map.put("package", req.packageValue);
        map.put("noncestr", req.nonceStr);
        map.put("timestamp", req.timeStamp);

        ArrayList<String> sortList = new ArrayList<>();
        sortList.add("appid");
        sortList.add("partnerid");
        sortList.add("prepayid");
        sortList.add("package");
        sortList.add("noncestr");
        sortList.add("timestamp");
        sort(sortList);

        String md5 = "";
        int size = sortList.size();
        for (int k = 0; k < size; k++) {
            if (k == 0) {
                md5 += sortList.get(k) + "=" + map.get(sortList.get(k));
            } else {
                md5 += "&" + sortList.get(k) + "=" + map.get(sortList.get(k));
            }
        }
        String stringSignTemp = md5+"&key=商户密钥";

        String sign=MD5.Md5(stringSignTemp).toUpperCase();

        return sign;
    }

    private String getRoundString() {

        Random random = new Random();

        return random.nextInt(10000) + "";
    }

    private String getTimeStamp() {
        return new Date().getTime() / 10 + "";
    }


    private static void sort(ArrayList<String> strings) {
        Collections.sort(strings);
    }
}

那么在APP中这样使用就好

WX_Pay pay = new WX_Pay(getContext());
pay.pay(str1,str2,str3);

5,错误小结:本人在开发中遇到过两个错误 
a,无法起调微信支付界面:错误原因是没有将WXPayEntryActivity 类放在在wxapi包下。 
b,支付失败:错误原因是在签名算法中忘记将商户密钥签名。 
这里基本可以总结下错误类型: 
1,起调失败:工程配置错误 
2,支付订单报错:订单信息错误 
c,千万注意微信支付要编译为release版才能通过支付测试

猜你喜欢

转载自blog.csdn.net/qq_15059163/article/details/80562171
今日推荐