纯安卓端开发微信支付

无服务器后台下进行微信支付集成

本篇博客主要介绍在没有后台的情况下,进行微信支付开发,涉及微信的统一下单、签名以及调取支付,文末附有demo

主要内容

  • 一键下单

  • 签名及二次签名

  • 调取支付

  • 其它设置

一键下单

参数说明,可以参考:[微信支付一键下单](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1)这里面的坑虽多,比如金额的单位是分,必须为整数,但是都比较容易解决,如果有错误,在下单时也会有返回的错误信息。如果参数没错,可以进行以下步骤:
1.设置下单数据
这里需要从商户管理这,拿到三个数据:应用id、商户号、API密钥
其它数据,可以自己产生,我将这些数据放入一个map数组,参考下面代码:
public Map<String, String> requestArgs() {
        String ip = NetUtil.getWifiIp(mInstance);
        if (TextUtils.isEmpty(ip)) {
            ip = NetUtil.getLocalIpAddress();
        }
        try {
            Map<String, String> requestMap = new HashMap<>();
            requestMap.put("appid", WXConfig.APP_ID); //应用ID
            requestMap.put("mch_id", WXConfig.MCH_ID); //商户号
            //requestMap.put("device_info", "013467007045764"); //设备号
            requestMap.put("nonce_str", WXConfig.genNonceStr()); //随机字符串
            //requestMap.put("sign_type", WXConfig.SIGN_TYPE);//签名类型
            requestMap.put("body", getString(R.string.app_name) + "-" + payBody);//商品描述
            //requestMap.put("detail","");//商品详情
            //requestMap.put("attach","测试");//附加数据
            requestMap.put("out_trade_no", WXConfig.genOutTradNo());//商户订单号
            //requestMap.put("fee_type","CNY");//货币类型
            requestMap.put("total_fee", payMoney);//总金额
            requestMap.put("spbill_create_ip", ip);//终端IP
            //requestMap.put("time_start","");//交易起始时间
            //requestMap.put("time_expire","");//交易结束时间
            //requestMap.put("goods_tag","");//商品标记
            requestMap.put("notify_url", WXConfig.notify_url);//通知地址
            requestMap.put("trade_type", WXConfig.trade_type);//交易类型
            //requestMap.put("limit_pay","no_credit");//指定支付方式
            return requestMap;
        } catch (Exception e) {
            Log.e("TAG", "fail, ex = " + e.getMessage());
            return null;
        }

上述代码中,如果被我注释了,说明这个参考可以为空,其中,需要自己得到的参数,我一一做出解释:
1.body-商品描述:遵循微信支付的要求为:应用名+说明
2.商户订单号:可以自行随机生成,保证唯一即可,我的生成规则如下:

    public static String genOutTradNo() {
        long currentTime = System.currentTimeMillis();
        Random random = new Random();
        return currentTime + random.nextInt(1000) + "";
    }

3.total_fee-总金额:即调用支付时应支付的价格,单位分,例100为1元
4.ip-终端IP:即下单终端的ip地址,可以通过以下方式获得(记得加入权限,可以参考demo中的权限):

    // 获取WIFI的ip地址
    public static String getWifiIp(Context context) {
        //获取wifi服务
        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        //判断wifi是否开启
        if (!wifiManager.isWifiEnabled()) {
            wifiManager.setWifiEnabled(true);
        }
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        int ipAddress = wifiInfo.getIpAddress();
        String ip = intToIp(ipAddress);
        return ip;
    }

    private static String intToIp(int i) {

        return (i & 0xFF) + "." +
                ((i >> 8) & 0xFF) + "." +
                ((i >> 16) & 0xFF) + "." +
                (i >> 24 & 0xFF);
    }

    public static String getLocalIpAddress() {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress()) {
                        return inetAddress.getHostAddress().toString();
                    }
                }
            }
        } catch (SocketException ex) {
        }
        return null;
    }

5.notify_url-通知地址:有便填上,没有随意填写正确网址
6.trade_type-交易类型:此处为APP,注意大写

2.提交下单
下单请求应在异步中进行,参考下面代码,发送post请求,即可
    private class GetPrepayIdTask extends AsyncTask<Void, Void, Map<String, String>> {

        @Override
        protected void onPreExecute() {
        }

        @Override
        protected void onPostExecute(Map<String, String> result) {
            // 调取支付
            payWx(result.get("prepay_id"));
        }

        @Override
        protected Map<String, String> doInBackground(Void... params) {

            // 统一下单的需要的数据
            Map<String, String> dataMap = requestArgs();

            //计算签名
            String sign = WXConfig.getSign(dataMap);
            dataMap.put("sign", sign);

            // 将map转为xml
            String entity = StringUtil.map2xml(dataMap);

            // 提交数据,获取下单信息
            byte[] buf = WXNetUtil.httpPost(WXConfig.unifiedorder, entity);
            assert buf != null;
            String content = new String(buf);
            LogUtil.d("content=" + content);

            // 将xml解析成map
            return StringUtil.xml2Map(content);
        }
    }

此处容易造成大部分困扰就是签名,关于签名,其实本质并不复杂,主要问题在于,一定要保证appid,商户号和key正确,key不正确微信是没有提示的,一定注意是32为的数字字母(含大小写),如果下单一直提示签名错误,请检查这三个参数。参数签名,可以参考以下代码:

    // 生成签名
    public static String getSign(Map<String, String> dataMap) {

        // 按字典排序
        Collection<String> keySet = dataMap.keySet();
        List<String> list = new ArrayList<>(keySet);
        Collections.sort(list);

        // 获取签名
        StringBuilder sb = new StringBuilder();
        for (String s : list) {
            if (sb.length() != 0) {
                sb.append("&").append(s).append("=").append(dataMap.get(s));
            } else {
                sb.append(s).append("=").append(dataMap.get(s));
            }
        }


        sb.append("&key=").append(WXConfig.API_KEY);

        LogUtil.d("nweS=" + sb.toString());

        // 最终发送的数据
        String sign = MD5.getMessageDigest(sb.toString()).toUpperCase();
        LogUtil.d("sign=" + sign);

        return sign;
    }

签名后,还要将map转为xml,得到结果后还要xml转为map,参考下面代码:

 /**
     * 将map转为xml
     * @param map 需要转换的map集合
     * @return
     */
    public static String map2xml(Map<String, String> map) {
        StringBuffer sb = new StringBuffer();
        sb.append("<xml>");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            sb.append("<" + entry.getKey() + ">" + entry.getValue() + "</" + entry.getKey() + ">");
        }
        sb.append("</xml>");
        return sb.toString();
    }

    /**
     * 将xml转为map
     * @param content 需要转换的xml数据
     * @return
     */
    public static Map<String, String> xml2Map(String content) {

        try {
            Map<String, String> 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.e("orion", e.toString());
        }
        return null;

    }

经过上列步骤,便能得到最重要的数据:prepay_id,也就是说,我们已经将后台的工作几乎完成了,那么下面就要涉及到安卓的工作了。

2.调用支付

1.导入微信支付包,可以在官网下载:libs下载
2.设置支付参数,参数参考官网说明:支付参数说明
其主要代码,见下:

 private void payWx(String prepayId) {

        PayReq payReq = new PayReq();
        payReq.appId = WXConfig.APP_ID;
        payReq.partnerId = WXConfig.MCH_ID;
        payReq.prepayId = prepayId;
        payReq.packageValue = "Sign=WXPay";
        payReq.nonceStr = WXConfig.genNonceStr();
        payReq.timeStamp = String.valueOf(TimeUtil.getTimestamp() / 1000);

        // 获取签名
        Map<String, String> signMap = new HashMap<>();
        signMap.put("appid", payReq.appId);
        signMap.put("partnerid", payReq.partnerId);
        signMap.put("prepayid", payReq.prepayId);
        signMap.put("package", payReq.packageValue);
        signMap.put("noncestr", payReq.nonceStr);
        signMap.put("timestamp", payReq.timeStamp);

        payReq.sign = WXConfig.getSign(signMap);


        msgApi.sendReq(payReq);
    }

msgApi一定要在调用前初始化:

    private void initWx() {
        msgApi = WXAPIFactory.createWXAPI(mInstance, null);
        // 将该app注册到微信
        msgApi.registerApp(WXConfig.APP_ID);
    }

在这个过程中,还要做的是,在AndroidManifest的主Activity中加入:

 <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="@string/wx_app_id" />
            </intent-filter>

加入receiver

        <receiver android:name=".AppRegister">
            <intent-filter>
                <action android:name="com.tencent.mm.plugin.openapi.Intent.ACTION_REFRESH_WXAPP" />
            </intent-filter>
        </receiver>

3.设置回调类:wxapi.WXPayEntryActivity

     private IWXAPI iwxapi;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        iwxapi = WXAPIFactory.createWXAPI(this, WXConfig.APP_ID);
        iwxapi.handleIntent(getIntent(), this);
    }

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


    @Override
    public void onReq(BaseReq req) {
    }

    @Override
    public void onResp(final BaseResp resp) {
        LogUtil.i(TAG, resp.errCode + "--" + resp.errStr);
    }
}

在onResp中进行判断和逻辑操作即可。
demo地址http://download.csdn.net/detail/dreamchou/9797821
说明:1.HttpPost要用到org.apache.http.legacy包,demo中也含该包
2.替换wxconfig中的appid,mch_id和api_key及value/string资源中的appid后,该项目可以直接运行(保证wxapi所在目录在项目报名下)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值