遵循:BY-SA

署名-相同方式共享 4.0协议

作者:谭东

时间:2016年10月28日

环境:Windows 7

Android版微信支付官方文档和Demo问题很多,官方也没有及时更新和细化开发集成文档。

这里分享我集成Android客户端微信支付的思路和部分代码。希望对大家有帮助。


遇到的问题无非以下几种:

1、提示签名不对;

2、打包签名后的APK无法调起微信支付客户端,直接返回回调页;

3、不支持中文的Body;

4、支付的钱倍数不对,因为微信的单位是分;

... ...

首先,本文针对的是最新版微信Android支付SDK3.1.1写的,大家可以放心使用。


首先,把WXPayEntryActivity.java复制到我们的包名下的.wxapi目录下,要一致。

 

 WXPayEntryActivity.java官方Demo里有,我这里也复制一份我的,仅供参考。   

public class WXPayEntryActivity extends BaseActivity implements IWXAPIEventHandler {    private IWXAPI api;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        api = WXAPIFactory.createWXAPI(this, Conf.APP_ID);        api.handleIntent(getIntent(), this);        getSupportActionBar().setIcon(R.drawable.logo_gray);        getSupportActionBar().setTitle("购买积分");        getSupportActionBar().setDisplayHomeAsUpEnabled(true);        Drawable dw = getResources().getDrawable(R.drawable.color_bg);        getSupportActionBar().setBackgroundDrawable(dw);    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        if (item.getItemId() == android.R.id.home) {            this.finish();        }        return super.onOptionsItemSelected(item);    }    @Override    protected void onNewIntent(Intent intent) {        super.onNewIntent(intent);        setIntent(intent);        api.handleIntent(intent, this);    }    @Override    public void onReq(BaseReq baseReq) {    }    @Override    public void onResp(BaseResp baseResp) {        if (baseResp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {            Log.d("info", "onPayFinish,errCode=" + baseResp.errCode);            if (baseResp.errCode == 0) {                ToastUtil.showToast(this, "支付成功");                Intent intent = new Intent("ACTION_PAY");                sendBroadcast(intent);                this.finish();            } else if (baseResp.errCode == -1) {                ToastUtil.showToast(this, "配置错误");                this.finish();            } else if (baseResp.errCode == -2) {                ToastUtil.showToast(this, "用户取消");                this.finish();            }        } else {            ToastUtil.showToast(this, baseResp.errStr);        }    }}

这里说一下Android微信支付流程,这里本地客户端都处理了所有的流程。

客户端把所有需要的信息拼接为XML后,统一下单,发送给微信服务器API请求,获取订单id,也就是prepayId。获取到这个prepayId后,我们再把必要的数据字段sign签名MD5后,调起微信支付客户端就可以了。比支付宝麻烦很多。


1、在程序的启动页,例如欢迎页就可以注册微信API的ID了。

IWXAPI msgApi = WXAPIFactory.createWXAPI(this, Conf.APP_ID);        msgApi.registerApp(Conf.APP_ID);


完毕。

2、接下来处理微信POST请求的必要参数的拼接和加密等处理。写PayActivity.java

统一下单获取prepayId的接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder

微信文档地址:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1

把必填的参数处理下:

nonce_str:随机字符串,不长于32位。

out_trade_no:商户系统内部的订单号,32个字符内、可包含字母。

spbill_create_ip:用户端实际ip,16位,可写固定值。

notify_url:暂时写成固定的,http://www.weixin.qq.com/wxpay/pay.php。

最麻烦的,sign:32位签名,参照签名生成算法。

以上这些算法我都会写成工具类,供大家参考。

签名算法:



附上整个支付代码:

PayActivity.java里关于支付的代码:

private IWXAPI api;    private String stringA;    private String noneString;    private String out_trade_no;    private String sign;    private String prepayId;    private String ip;    private String body = "商品-积分充值";    private static final String order_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//POST请求统一下单接口地址    private String notify_url = "http://www.weixin.qq.com/wxpay/pay.php";    private String entity;//XML形式的post请求实体    private void payWeixin(int money) {        api = WXAPIFactory.createWXAPI(this, Conf.APP_ID);        try {            body = new String(body.getBytes(), "utf-8");        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        }        noneString = Utils.createRandomString(true, 32);//32位随机字符串        out_trade_no = Utils.createRandomString(true, 20) + Utils.timeStamp();//30位内部随机订单号        ip = "123.123.123.123";        stringA = "appid=" + Conf.APP_ID + "&body=" + body + "&mch_id=" + Conf.MCH_ID + "&nonce_str=" + noneString + "¬ify_url=" + notify_url + "&out_trade_no=" + out_trade_no + "&spbill_create_ip=" + ip + "&" +                "total_fee=" + money + "&trade_type=APP";        String stringSignTemp = stringA + "&key=" + Conf.KEY;        sign = Utils.getMD5(stringSignTemp).toUpperCase(Locale.getDefault());        entity = "" + Conf.APP_ID + "" + Conf.MCH_ID + "" + noneString + "" + sign +                "" + body + "" + out_trade_no + "" + money +                "" + ip + "" + notify_url + "APP";        try {            entity = new String(entity.getBytes(), "ISO8859-1");//想要支持中文的Boby,那就要把XML转码为ISO8859-1即可        } catch (UnsupportedEncodingException e) {            e.printStackTrace();        }        new Thread(new Runnable() {            @Override            public void run() {                try {                    byte[] buf = Utils.httpPost(order_url, entity);                    Message message = new Message();                    message.what = 0;                    message.obj = buf;                    handler.sendMessage(message);                } catch (Exception e) {                    Log.e("info", "异常:" + e.getMessage());                }            }        }).start();    }    Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what) {                case 0:                    byte[] buf = (byte[]) msg.obj;                    if (buf != null && buf.length > 0) {                        String content = new String(buf);                        OrderResult orderResult = Tools.parseXml(new ByteArrayInputStream(content.getBytes()));                        if (!TextUtils.equals(orderResult.getReturnCode(), "SUCCESS")) {                            Toast.makeText(PayActivity.this, orderResult.getReturnMsg(), Toast.LENGTH_SHORT).show();                            return;                        }                        if (!TextUtils.equals(orderResult.getResultCode(), "SUCCESS")) {                            Toast.makeText(PayActivity.this, orderResult.getErrorDesc(), Toast.LENGTH_SHORT).show();                            return;                        }                        PayReq req = new PayReq();                        req.appId = Conf.APP_ID;                        req.partnerId = Conf.MCH_ID;                        req.prepayId = orderResult.getPrepayId();                        req.packageValue = "Sign=WXPay";                        req.nonceStr = noneString;                        String timeStamp = Utils.timeStamp();                        req.timeStamp = timeStamp;                        req.sign = Utils.getMD5("appid=" + Conf.APP_ID + "&noncestr=" + noneString + "&package=Sign=WXPay" +                                "&partnerid=" + Conf.MCH_ID + "&prepayid=" + orderResult.getPrepayId() + "×tamp=" + timeStamp + "&key=" + Conf.KEY).toUpperCase(Locale.getDefault());                        api.sendReq(req);                    } else {                        Log.d("PAY_GET", "服务器请求错误");                        Toast.makeText(PayActivity.this, "服务器请求错误", Toast.LENGTH_SHORT).show();                    }                    break;            }        }    };

Utils工具类代码:

public class Utils {    /**     * 产生随机字符串     *     * @param numberFlag 是否允许有字母     * @param length     随机字符串长度     * @return 随机字符串     */    public static String createRandomString(boolean numberFlag, int length) {        String retStr = "";        String strTable = numberFlag ? "1234567890" : "1234567890abcdefghijkmnpqrstuvwxyz";        int len = strTable.length();        boolean bDone = true;        do {            retStr = "";            int count = 0;            for (int i = 0; i < length; i++) {                double dblR = Math.random() * len;                int intR = (int) Math.floor(dblR);                char c = strTable.charAt(intR);                if (('0' <= c) && (c <= '9')) {                    count++;                }                retStr += strTable.charAt(intR);            }            if (count >= 2) {                bDone = false;            }        } while (bDone);        return retStr;    }    /**     * 获取MD5加密后的字符串     *     * @param val 待加密字符串     * @return 加密后字符串     */    public static String getMD5(String val) {        byte[] m = new byte[0];        try {            MessageDigest md5 = MessageDigest.getInstance("MD5");            md5.update(val.getBytes());            m = md5.digest();        } catch (NoSuchAlgorithmException e) {            e.printStackTrace();        }        return getString(m);    }    private static String getString(byte[] b) {        StringBuffer sb = new StringBuffer();        for (int i = 0; i < b.length; i++) {            sb.append(b[i]);        }        return sb.toString();    }    /**     * 获取10位长度的时间戳     *     * @return 10位时间戳     */    public static String timeStamp() {        String time = String.valueOf(System.currentTimeMillis());        return time.substring(0, 10);    }    public static byte[] httpGet(final String url) {        if (url == null || url.length() == 0) {            return null;        }        HttpClient httpClient = getNewHttpClient();        HttpGet httpGet = new HttpGet(url);        try {            HttpResponse resp = httpClient.execute(httpGet);            if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {                return null;            }            return EntityUtils.toByteArray(resp.getEntity());        } catch (Exception e) {            e.printStackTrace();            return null;        }    }    public static byte[] httpPost(String url, String entity) {        if (url == null || url.length() == 0) {            return null;        }        HttpClient httpClient = getNewHttpClient();        HttpPost httpPost = new HttpPost(url);        try {            httpPost.setEntity(new StringEntity(entity));            httpPost.setHeader("Accept", "application/json");            httpPost.setHeader("Content-type", "application/json");            HttpResponse resp = httpClient.execute(httpPost);            if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {                return null;            }            return EntityUtils.toByteArray(resp.getEntity());        } catch (Exception e) {            e.printStackTrace();            return null;        }    }    private static HttpClient getNewHttpClient() {        try {            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());            trustStore.load(null, null);            SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);            sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);            HttpParams params = new BasicHttpParams();            HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);            HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);            SchemeRegistry registry = new SchemeRegistry();            registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));            registry.register(new Scheme("https", sf, 443));            ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);            return new DefaultHttpClient(ccm, params);        } catch (Exception e) {            return new DefaultHttpClient();        }    }    public static OrderResult parseXml(InputStream is) {        XmlPullParser parser = Xml.newPullParser();        OrderResult orderResult = null;        try {            parser.setInput(is, "UTF-8");            int type = parser.getEventType();            while (type != XmlPullParser.END_DOCUMENT) {                switch (type) {                    case XmlPullParser.START_DOCUMENT:                        break;                    case XmlPullParser.START_TAG:                        if (parser.getName().equals("xml")) {                            orderResult = new OrderResult();                        } else if (parser.getName().equals("return_code")) {                            orderResult.setReturnCode(parser.nextText());                        } else if (parser.getName().equals("return_msg")) {                            orderResult.setReturnMsg(parser.nextText());                        } else if (parser.getName().equals("result_code")) {                            orderResult.setResultCode(parser.nextText());                        } else if (parser.getName().equals("err_code_des")) {                            orderResult.setErrorDesc(parser.nextText());                        } else if (parser.getName().equals("prepay_id")) {                            orderResult.setPrepayId(parser.nextText());                        } else if (parser.getName().equals("sign")) {                            orderResult.setSign(parser.nextText());                        }                        break;                    case XmlPullParser.END_TAG:                        break;                }                type = parser.next();            }        } catch (XmlPullParserException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        return orderResult;    }}

这样整个微信Android支付集成就完毕了。




更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. 分支和循环(二)(零基础学习C语言)
  3. Nginx系列教程(二)| 一文带你读懂Nginx的正向与反向代理
  4. RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
  5. 【资源介绍】iGou Android(安卓)开发视频教程
  6. Android(安卓)Intent用法总结
  7. PC客户端与Android服务端的Socket同步通信
  8. Android(安卓)自定义 View 之使用 SurfaceView 实现动画
  9. Android(安卓)NDK下编译google protocol buffer(protobuf)

随机推荐

  1. [Unity][PHOTON][UNET][SOCKET][Android]
  2. Android之查找apk包名和启动入口类
  3. Android远程数据库通信实现
  4. 使用android studio 报错 undefined refe
  5. Android 桌面组件【widget】初探
  6. Android系统级深入开发输入系统
  7. IOS录制的视频在Android播放异常的问题
  8. 源码角度分析Android的事件输入系统(input
  9. android bitmap 缓存实现(OOM)
  10. Android中的服务有几种,具体实现步骤,这几