移动支付(微信、支付宝、银联)集成

微信支付

Demo一直返回-1

先去官网下载demo运行,第一次可以支付成功,以后就一直返回-1
如果是以客户端的官网demo进行测试的话,若一直返回-1,可采用如下方式处理:

  • 删除微信缓冲数据
    按如下步骤:设置->应用程序管理器->微信->清除数据
  • 使用demo里的debug.keystore来测试
    功能修改步骤:preferences->android->build->custom debug keystore->browse。

    集成之后总是返回-1

    可能产生问题的地方:包名不一致等其它原因(按照集成申请步骤来,这个应该很少发生)

    Demo中的订单模拟

    其实微信支付的大部分工作是需要服务器端进行完成,微信支付的demo中,帮我们模拟了通过服务器生成订单,并返回prepayid的过程,然后demo中会拿着这些微信给我们模拟好的数据去进行支付,每次测试会向”微信测试”支付0.01元。demo中毕竟是模拟的,我们自己的集成一定是实实在在的,不过这里我并不会涉及到太多关于服务器端需要做什么

    客户端快速集成

    微信支付的demo比较冗余,我们把支付需要的东西单独摘出来,画箭头的文件可以copy到自己的工程中,根据实际业务或者代码设计进行修改。另外需要注意如下几点:
  • AndroidManifest.xml中package名字和项目包名一样;
  • 将WXPayEntryActivity.java放在package.wxapi/下面
  • AndroidManifest.xml中添加.wxapi.WXPayEntryActivity(不添加,支付成功后无法跳转到相应的通知Activity界面);

这里写图片描述

集成关键步骤

从实际开发的角度来看PayActivity中需要添加的三个必备参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*****************************************************************
* 微信支付需要的参数解释: APP_ID,商户号,API密钥
*****************************************************************/
// APP_ID
public static final String APP_ID = "xxxxxxxxxxxxxx";
// 商户号
public static final String MCH_ID = "yyyyyyyyyyyyyy";
// API密钥,在商户平台设置
public static final String API_KEY = "zzzzzzzzzzzzz";
IWXAPI msgApi ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
msgApi = WXAPIFactory.createWXAPI(this.getApplicationContext(), null);
...
}

基本流程

  • 点击微信支付->检测微信是否安装
  • 未安装toast提示
  • 已安装->根据需要兑换或者支付的产品id去服务器请求创建订单->创建订单成功->根据服务器返回的prepayid调用微信客户端进行微信支付->微信回调(无论成功、失败或者取消)->返回支付页面->去服务器校验订单->成功则根据校验结果进行相应业务跳转,校验异常可以强制用户进入订单列表页面进行再次比对校验(这个过程需要本地保存一下校验异常的订单,不过具体怎么处理校验异常可以自行制定策略)
  • 一般校验订单是发生在微信客户端回调成功之后再去校验,但有一种情况也需要去校验,就是用户不是通过在微信中操作返回支付页面,而是通过home按键返回,这样支付页面就收不到微信的回调结果,即使已经支付成功,所以我们需要在支付页面回到前台的某个控制时机去再次校验一下订单
  • 另外微信支付客户端的回调是在WXPayEntryActivity中完成的,这里可以自己再进一步封装处理

贴一些相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* 微信支付使用
* @return
*/
private String genNonceStr() {
Random random = new Random();
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
/**
* 微信安装检测
* @return
*/
private boolean checkApkExist() {
if (msgApi.isWXAppInstalled()) {
return true;
} else {
return false;
}
}
/**
* 微信支付:调用微信支付
* @param prepayId 支付id
*/
private void weiXinPay(String prepayId) {
PayReq req = new PayReq();
req.appId = APP_ID;
req.partnerId = MCH_ID;
req.prepayId = prepayId;
req.packageValue = "Sign=WXPay";
req.nonceStr = genNonceStr();
req.timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
StringBuilder sb = new StringBuilder();
sb.append("appid=");
sb.append(req.appId);
sb.append("&noncestr=");
sb.append(req.nonceStr);
sb.append("&package=");
sb.append(req.packageValue);
sb.append("&partnerid=");
sb.append(req.partnerId);
sb.append("&prepayid=");
sb.append(req.prepayId);
sb.append("&timestamp=");
sb.append(req.timeStamp);
sb.append("&key=");
sb.append(API_KEY);
String appSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
req.sign = appSign;
msgApi.registerApp(APP_ID);
msgApi.sendReq(req);
}

支付宝支付

集成过程

其实支付宝支付的集成更加简单
这里写图片描述

支付宝支付的demo上来是不能运行的,因为你缺少需要的参数,这里列举的是客户端需要的

1
2
3
4
5
6
7
8
9
/*****************************************************************
* 支付宝支付需要的参数解释: 商户PID、商户收款账号、商户私钥
*****************************************************************/
// 商户PID
public static final String PARTNER = "xxxxxxxxxxxxx";
// 商户收款账号
public static final String SELLER = "yyyyyyyyyyy";
// 商户私钥,pkcs8格式
public static final String RSA_PRIVATE = "zzzzzzzzz";

配置文件中不要忘记加入:(否则当手机没有安装支付宝客户端时无法调用支付成功)

1
2
3
4
5
6
7
<!-- alipay sdk begin -->
<activity
android:name="com.alipay.sdk.app.H5PayActivity" android:configChanges="orientation|keyboardHidden|navigation|screenSize"
android:exported="false"
android:screenOrientation="behind" android:windowSoftInputMode="adjustResize|stateHidden" >
</activity>
<!-- alipay sdk end -->

贴一下相关代码,较demo有些许改动:

注意事项

需要注意的地方:

  • 私钥一定配置正确,我看有好多小伙伴会在sign(orderInfo)时获得的sign为null,最终导致做URL编码的时候报空指针,我也碰到过,就是服务器那边给的RSA_PRIVATE有问题。公钥客户端用不着
  • 微信支付只需要传递一个prepayid,而支付宝支付需要穿三个参数,一个是订单号,一个是价格,一个是notify_url
  • 支付宝支付创建订单以及订单校验的过程与微信支付类似,因为同在一个地方,要控制好相应的逻辑
  • 注意一下price的单位,分和元不要搞错了
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    /**
    * call alipay sdk pay. 调用支付宝SDK支付
    */
    public void alipay(String tradeNo, String price, String notify_url) {
    // 订单
    // String orderInfo = getOrderInfo("测试的商品", "该测试商品的详细描述", "0.01");
    String orderInfo = getOrderInfo(tradeNo, title,
    payInfo, price, notify_url);
    // 对订单做RSA 签名
    String sign = sign(orderInfo);
    try {
    // 仅需对sign 做URL编码
    sign = URLEncoder.encode(sign, "UTF-8");
    } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
    }
    // 完整的符合支付宝参数规范的订单信息
    final String payInfo = orderInfo + "&sign=\"" + sign + "\"&" + getSignType();
    Runnable payRunnable = new Runnable() {
    @Override
    public void run() {
    // 构造PayTask 对象
    PayTask alipay = new PayTask(PayActivity.this);
    // 调用支付接口,获取支付结果
    String result = alipay.pay(payInfo);
    Message msg = new Message();
    msg.what = SDK_PAY_FLAG;
    msg.obj = result;
    mHandler.sendMessage(msg);
    }
    };
    // 必须异步调用
    ThreadPoolUtils.execute(payRunnable);
    }
    /**
    * create the order info. 创建支付宝订单信息
    */
    public String getOrderInfo(String tradeNo, String subject, String body, String price, String notify_url) {
    // 签约合作者身份ID
    String orderInfo = "partner=" + "\"" + PARTNER + "\"";
    // 签约卖家支付宝账号
    orderInfo += "&seller_id=" + "\"" + SELLER + "\"";
    // 商户网站唯一订单号
    orderInfo += "&out_trade_no=" + "\"" + tradeNo + "\"";
    if (!TextUtils.isEmpty(subject)) {
    // 商品名称
    orderInfo += "&subject=" + "\"" + subject + "\"";
    }
    if (!TextUtils.isEmpty(body)) {
    // 商品详情
    orderInfo += "&body=" + "\"" + body + "\"";
    }
    // 商品金额
    orderInfo += "&total_fee=" + "\"" + (Integer.parseInt(price) / 100f) + "\"";
    // 服务器异步通知页面路径
    orderInfo += "&notify_url=" + "\"" + notify_url + "\"";
    // 服务接口名称, 固定值
    orderInfo += "&service=\"mobile.securitypay.pay\"";
    // 支付类型, 固定值
    orderInfo += "&payment_type=\"1\"";
    // 参数编码, 固定值
    orderInfo += "&_input_charset=\"utf-8\"";
    // 设置未付款交易的超时时间
    // 默认30分钟,一旦超时,该笔交易就会自动被关闭。
    // 取值范围:1m~15d。
    // m-分钟,h-小时,d-天,1c-当天(无论交易何时创建,都在0点关闭)。
    // 该参数数值不接受小数点,如1.5h,可转换为90m。
    orderInfo += "&it_b_pay=\"30m\"";
    // extern_token为经过快登授权获取到的alipay_open_id,带上此参数用户将使用授权的账户进行支付
    // orderInfo += "&extern_token=" + "\"" + extern_token + "\"";
    // 支付宝处理完请求后,当前页面跳转到商户指定页面的路径,可空
    orderInfo += "&return_url=\"m.alipay.com\"";
    // 调用银行卡支付,需配置此参数,参与签名, 固定值 (需要签约《无线银行卡快捷支付》才能使用)
    // orderInfo += "&paymethod=\"expressGateway\"";
    return orderInfo;
    }
    /**
    * 支付宝支付使用 sign the order info. 对订单信息进行签名
    *
    * @param content
    * 待签名订单信息
    */
    public String sign(String content) {
    return SignUtils.sign(content, RSA_PRIVATE);
    }
    /**
    * 支付宝支付使用 get the sign type we use. 获取签名方式
    */
    public String getSignType() {
    return "sign_type=\"RSA\"";
    }

支付宝支付成功的回调(收到的msg.what为SDK_PAY_FLAG)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* 支付宝支付客户端回调成功
* @param msg
* @param theLayout
*/
private static void aliSdkPaySuc(Message msg, PayActivity theLayout) {
PayResult payResult = new PayResult((String) msg.obj);
// 支付宝返回此次支付结果及加签,建议对支付宝签名信息拿签约时支付宝提供的公钥做验签
String resultInfo = payResult.getResult();
String resultStatus = payResult.getResultStatus();
// 判断resultStatus 为“9000”则代表支付成功,具体状态码代表含义可参考接口文档
if (TextUtils.equals(resultStatus, "9000")) {
/*Toast.makeText(theLayout, "支付成功",
Toast.LENGTH_SHORT).show();*/
theLayout.payCheck();
} else {
// 判断resultStatus 为非“9000”则代表可能支付失败
// “8000”代表支付结果因为支付渠道原因或者系统原因还在等待支付结果确认,最终交易是否成功以服务端异步通知为准(小概率状态)
if (TextUtils.equals(resultStatus, "8000")) {
Toast.makeText(theLayout, "支付结果确认中",
Toast.LENGTH_SHORT).show();
} else if (TextUtils.equals(resultStatus, "4000")) {
// 其他值就可以判断为支付失败,包括用户主动取消支付,或者系统返回的错误
Toast.makeText(theLayout, "支付失败",
Toast.LENGTH_SHORT).show();
} else if(TextUtils.equals(resultStatus, "6001")){
theLayout.payCount("5","");
Toast.makeText(theLayout, "支付取消",
Toast.LENGTH_SHORT).show();
}else if(TextUtils.equals(resultStatus, "6002")){
Toast.makeText(theLayout, "网络连接出错",
Toast.LENGTH_SHORT).show();
}
}
theLayout.clickAliPay = false;
}

银联支付

银联商家技术服务SDK下载地址
Drawing

##小结

  • 关于微信支付和支付宝支付的坑,上面已经介绍很多了,如果还有问题的话,可以参考一下官方的FAQ
  • 集成起来整体感觉支付宝支付要比微信支付容易点
  • 微信支付可以支付虚拟货币,但是支付宝需要走特殊通道开通
  • 银联支付暂时没做,因为商家账户申请的时间好长
  • 移动支付的过程处理需要在逻辑上做到严谨(对各种异常要提前预判),毕竟是跟钱有关的
    这里写图片描述
SuS wechat
欢迎您扫一扫上面的微信公众号,订阅我的最新信息!
坚持原创技术分享,您的支持将鼓励我继续创作!