Android微信支付集成流程及其常见错误

[支付宝集成传送门:http://blog.csdn.net/hx7013/article/details/61476320]

一、你需要关心的东西

1.申请与认证

2.上传密钥(商户KEY),包名和签名的修改

3.服务端集成

4.安卓集成

本篇就全部写了吧,让我们一步步来。

二、申请与认证

1.提交您的开发者资料

微信开放平台: https://open.weixin.qq.com/
进入后申请账户 -> 创建移动应用 -> 开发者认证[300 RMB] -> 申请微信支付,基本上无坑点,公司资料齐全可以很顺利可以通过。

2.获得商户平台帐号

认证时你需要登陆 微信支付商户平台:https://pay.weixin.qq.com
提交腾讯打给你公司对公账户上的审核款,登陆微信商户平台的账户密码在开发者账户认证通过或会通过邮件发送到申请时填写的邮箱,不能自主注册。

三、上传密钥、配置包名、配置应用签名

Tips:本是很简单的功能,为何要单独列一个来说呢?
因为网上的其它教程基本上是老的提交方法了,当初在这也迷糊密钥在何处生成或提交。所以单独列出来图文说明。

1.配置商户KEY

登陆微信支付商户平台:https://pay.weixin.qq.com,用你获得的账户密码进行登陆。
然后点击 账户中心 -> API安全 -> 设置API密钥
微信支付集成
微信支付集成

Tips:密钥使用的是32位大小写+数字的密串,推荐自动生成。
百度[随机密码]很多的,注意只勾选大小写+数字,长度为32既可。
例如:bXubGehLcukfQc2OyP76qPs84LBuq8oO
当然你也可以自己填写,但是个人感觉随机密码相对更加安全。至少社工不能破掉。

2.设置包名

登陆微信开放平台: https://open.weixin.qq.com/
点击 管理中心 -> 选择应用点击查看 -> 在最下面有[开发信息] -> 点击修改填入包名。

当初审核时提交的是豌豆荚的App链接,所以包名和签名已自动填入。
如没有自动填入或错误,可以自行修改。

3.设置签名

Tips:在翻阅其它文档时说修改签名后需要审核,但是在测试中发现并不需要审核,估计是以前微信支付刚上线时有该限制。所以现在可以放心修改为Debug签名直接调试,免得先发布,在adb install...

同样和上一步设置包名一样,先登陆微信开发者平台,而后在 管理中心 -> 选择应用点击查看 -> 在最下面有[开发信息] -> 点击修改。

正式发布的签名和Debug签名可以通过几种方法获取:http://blog.csdn.net/hx7013/article/details/61671633

四、服务端的集成

服务端集成重点只说签名Sign的生成,其它都是傻瓜式集成,这点我们自己集成时也遇到了坑,所以单独说一下。
当初在集成服务端的时候调用的是微信提供的开发包,但不知为何在Android上调用后,总是弹不出支付的页面,调用时就闪退回来直接反 -1 错误(不得不吐槽一下,-1有那么多情况,就不能学支付宝弹个详细点的错误码吗?)耽搁半天,终于解决。

首先来看看微信提供的这个接口调试工具:https://pay.weixin.qq.com/wiki/tools/signverify/ (貌似IE打不开)

先来张图看看:
微信支付集成

Tips:不用纠结我为何没打码,因为都是虚假参数。但是签名的过程一样,大家能看懂既可。

首先进去后把接口方式从【提交被扫支付】改为【自定义】,然后填入appid、partnerid、prepayid、package、noncestr、timestamp和对应的值。在该调试工具页面,记住我强调是该调试页面提交这些参的顺序是不强求的,但是如果你是自己服务端签名的时候就一定得按顺序提交,这个顺序马上会说到。

填入值后点击生成签名,会发现下面有#1、#2、#3、#4四个流程,其实这个流程就是签名的过程。
认证看后会发现,其实就是参数排序连接,然后再连接商户Key,最后取MD5。

重点来了,也许你会纠结为何参数正确的,但是Sign签名一直不对呢?其实了解MD5的就应该知道,MD5是摘要算法,只知道通过文本去取Hash值。所以appid=123456&partnerid=123456 和 partnerid=123456&appid=123456计算出来的值一定是不同的。这也是坑之一,签名一定得按顺序组合后再取MD5值。

这个顺序就是微信接口调试工具中#2步骤中所提示的,从上图中我们可以看到我提交的是appid、partnerid、prepayid、package、noncestr、timestamp,但是微信给我组合成了appid、noncestr、package、partnerid、prepayid、timestamp、key。

appid=wx666888abc8defg88&noncestr=VCG4w95nS5Ku7mCi&package=Sign=WXPay&partnerid=1668866888&prepayid=wx20170312211500a8y53pgb0klvyh2rmoyu&timestamp=1489324509&key=UWMOIg1qJdHLZfEzAbm91BOBJG4oyC5C

而这个顺序就是签名通常出错的地方,自己在服务端生成签名时一不小心顺序出错,虽然参数正确,但是由于取出来的MD5是和微信那边取出的MD5是不同的,所以固然签名错误。
所以后面服务器生成签名的最简单解决方法就是硬接+取MD5

string signStr = "appid="+valAppid+"&noncestr="+valNoncestr+"&package=Sign=WXPay&partnerid="+valPartnerid+"&prepayid="+valPrepayid+"&timestamp="+valTimestamp+"&key="+valKEY
//c#的取MD5方法,其它语言自行Google
string signMd5 = FormsAuthentication.HashPasswordForStoringInConfigFile(signStr , "MD5") //signMd5即为最终签名

获得Sign后Android提交调用既可。

五、Android的集成

1.Manifest的声明

<!-- 这个也可以不设置,只是习惯跟着官方的Demo了,顺便填写了一下appid,也就是data android:scheme="wx666888abc8defg88" -->
 <application
   android:name="com.wx.wxTest.xApplication"
   android:allowBackup="true"
   android:icon="@drawable/app_icon"
   android:label="@string/app_name"
   android:screenOrientation="portrait"
   android:theme="@style/AppTheme" >
      <activity
        android:name="com.wx.wxTest.Activity.Welcome"
        android:screenOrientation="portrait"
        android:theme="@style/Welcome" >
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
          <intent-filter>
            <action android:name="android.intent.action.VIEW" />
              <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="wx666888abc8defg88" />
          </intent-filter>
      </activity>

<!-- 该activity是申明给微信回调时通知的Activity -->
      <activity
        android:name="com.wx.wxTest.wxapi.WXPayEntryActivity"
        android:exported="true"
        android:launchMode="singleTop"
        android:screenOrientation="portrait" >
      </activity>

又一个坑点来了,我们先来看看官方的文档。
微信支付集成

什么叫“在net.sourceforge.simcpux.wxapi包路径中实现WXPayEntryActivity类(包名或类名不一致会造成无法回调)“我就傻不拉唧的新建了一个包,名为net.sourceforge.simcpux.wxapi,再新建了一个Activity名为WXPayEntryActivity。后面果断调不起…后面尝试 自己的包名(也就是)Manifest中的package=”com.wx.wxTest”,组合为com.wx.wxTest.wxapi.WXPayEntryActivity就可以回调了。微信你不能说清楚是自己的包名+wxapi.WXPayEntryActivity吗?来个必须和你说的一致!

2.调用类Test_WxActivity

package com.wx.wxTest.activity;

import com.tencent.mm.opensdk.modelpay.PayReq;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.Toast;

public class Test_WxActivity extends Activity {
    private IWXAPI msgApi;
    private PayReq request;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 可以在Application中注册,也可以在这里注册
        // 如果在当前注册即为
        // msgApi = WXAPIFactory.createWXAPI(this, null);
        // msgApi.registerApp("wx666888abc8defg88");

        // 如果在Application注册即为,如果在当前注册请注释掉该代码
        msgApi = WXAPIFactory.createWXAPI(this, "wx666888abc8defg88");

        new Thread(wxPay).start();
    }

    Handler mainHandler = new Handler() {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
            case 100:

                Boolean mSendReq = msgApi.sendReq(request);
                Toast.makeText(Test_WxActivity.this, "调起微信sendReq=>" + mSendReq, Toast.LENGTH_SHORT).show();

                break;
            default:
                break;
            }

        };
    };

    Runnable wxPay = new Runnable() {

        @Override
        public void run() {
            // 这些值应该从服务器获取,这里只是为了演示
            request = new PayReq();

            request.appId = "wx666888abc8defg88";
            request.partnerId = "1668866888";
            request.prepayId = "wx20170312211500a8y53pgb0klvyh2rmoyu";
            request.nonceStr = "VCG4w95nS5Ku7mCi";
            request.timeStamp = "1489324509";
            request.packageValue = "Sign=WXPay";
            request.sign = "19890233F9E17CC54E0B6285032AE419";

            mainHandler.sendEmptyMessage(100);
        }
    };

}

3.回调类WXPayEntryActivity

package com.wx.wxTest.wxapi;

import com.jhx.hyxs.R;
import com.tencent.mm.opensdk.constants.ConstantsAPI;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {

    private IWXAPI api;

    private ImageView ivImg;
    private TextView tvText, tvSub;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wxentry);

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

        // 注意:
        // 第三方开发者如果使用透明界面来实现WXEntryActivity,需要判断handleIntent的返回值,如果返回值为false,则说明入参不合法未被SDK处理,应finish当前透明界面,避免外部通过传递非法参数的Intent导致停留在透明界面,引起用户的疑惑
        try {
            api.handleIntent(getIntent(), this);
        } catch (Exception e) {
            e.printStackTrace();
        }

        initView();
    }

    private void initView() {
        ((TextView) findViewById(R.id.head_title)).setText("支付结果");
        ((ImageView) findViewById(R.id.head_back)).setVisibility(View.INVISIBLE);

        ivImg = (ImageView) findViewById(R.id.wxentry_img);
        tvText = (TextView) findViewById(R.id.wxentry_text);
        tvSub = (TextView) findViewById(R.id.wxentry_sub);

        tvSub.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                finish();
            }
        });

    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        setIntent(intent);
        api.handleIntent(intent, this);
    }

    @Override
    public void onResp(BaseResp resp) {
        if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
            if (resp.errCode == 0) {
                Toast.makeText(this, "支付成功", Toast.LENGTH_LONG).show();
                ivImg.setImageResource(R.drawable.pay_success);
                tvText.setText("支付成功");
                tvText.setTextColor(Color.parseColor("#39BC32"));
            } else {
                Toast.makeText(this, "支付失败! (" + resp.errCode + ")", Toast.LENGTH_LONG).show();
                ivImg.setImageResource(R.drawable.pay_error);
                tvText.setText("支付失败");
                tvText.setTextColor(Color.parseColor("#FFA800"));
            }
        }
    }

    @Override
    public void onReq(BaseReq arg0) {
        // TODO Auto-generated method stub

    }
}

六、常见集成时遇到的坑和解决的方法

1. 微信回调errCode = -1

官方文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5
各种填坑后发现,这个可以相信官方文档,虽然模糊不清,但是说的的确大部分可能也都为“签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配”,再加一条包名错误(虽然可能性很低)
解决方法:
1. 检查在开发者平台设置的应用签名是否和当前APP签名一致(Debug签名或发布版签名)
2. 是否调用了注册微信的代码

final IWXAPI msgApi = WXAPIFactory.createWXAPI(this, null);
msgApi.registerApp("wx666888abc8defg88");

2.签名错误

一定得按照appid、noncestr、package、partnerid、prepayid、timestamp、key的顺序组合后取MD5。
例如:

appid=wx666888abc8defg88&noncestr=VCG4w95nS5Ku7mCi&package=Sign=WXPay&partnerid=1668866888&prepayid=wx20170312211500a8y53pgb0klvyh2rmoyu&timestamp=1489324509&key=UWMOIg1qJdHLZfEzAbm91BOBJG4oyC5C

3.回调错误

这个错误其实为微信的坑,是的,我就是那么直白!
解决方法:
别听信微信说的net.sourceforge.simcpux.wxapi包路径中实现WXPayEntryActivity类(包名或类名不一致会造成无法回调)。
而是使用你的包名+wxapi.WXPayEntryActivity,例如com.wx.wxTest.wxapi.WXPayEntryActivity

4.签名

说一个不是错误但一定得注意的地方,签名过程一定得在服务端完成!若你在客户端(如:Android)上完成时,你的商户KEY泄露可能很大(毕竟反调试Android并不是什么难事),而商户KEY的泄露将导致你的整个支付系统崩塌。

七、总结

微信支付的坑比支付宝支付的坑要多,大多数是因为官方的文档模糊不清,但是若按照我刚说的配置,基本没有问题。如果问题欢迎留言,定知无不言。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章