Android App接入支付功能——支付宝篇

/ Android / 没有评论 / 1887浏览

Android App接入支付功能——支付宝篇

微信支付,请参考我另一篇:Android App接入支付功能——微信篇

因为项目中用到支付功能,而且支付宝文档和微信文档写的很简洁,不仔细研究,真的无法集成成功

老样子,上效果图由于涉及到输入密码,我分开了

1

2

大概说下实现思路:

  1. 首先在官方注册成为开发者,创建应用(创建应用时,为您生成应用唯一标识(APPID)保存appid,下文要用到),并给应用添加App支付功能;(已是开发者请忽略)
  2. 导入官方的sdk,下载官方sdk;
  3. 配置相关的权限;
  4. 进行支付接口的调用;
  5. 支付结果获取和处理。(请注意:下面代码把⑤的步骤直接放在④里面了)

步骤

1.登录官网,也就是蚂蚁金服的开放平台,填写相关信息,进行注册账号进行登录,注册成为开发者(已是开发者请忽略)

3

2.下载官方sdk,将sdk放入自己工程libs文件中:

4

5

大家注意sdk包,可以看出该sdk包后缀是20170922,说明是此jar包发布的日期。

3.配置清单文件AndroidManifest.xml:

①添加Activity声明:

<!-- 若手机没有安装支付宝,则调用H5支付页面 -->
<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>
<activity
    android:name="com.alipay.sdk.app.H5AuthActivity"
    android:configChanges="orientation|keyboardHidden|navigation"
    android:exported="false"
    android:screenOrientation="behind"
    android:windowSoftInputMode="adjustResize|stateHidden" >
</activity>

②添加权限声明:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

如果想混淆代码,在工程proguard-rules.pro添加如下代码:

-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IAlixPay$Stub{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
-keep class com.alipay.sdk.app.PayTask{ public *;}
-keep class com.alipay.sdk.app.AuthTask{ public *;}
-keep class com.alipay.sdk.app.H5PayCallback {
    <fields>;
    <methods>;
}
-keep class com.alipay.android.phone.mrpc.core.** { *; }
-keep class com.alipay.apmobilesecuritysdk.** { *; }
-keep class com.alipay.mobile.framework.service.annotation.** { *; }
-keep class com.alipay.mobilesecuritysdk.face.** { *; }
-keep class com.alipay.tscenter.biz.rpc.** { *; }
-keep class org.json.alipay.** { *; }
-keep class com.alipay.tscenter.** { *; }
-keep class com.ta.utdid2.** { *;}
-keep class com.ut.device.** { *;}

4.调用支付接口: 在调用支付接口之前,先简单介绍下工程结构以及相关类代表含义:

 src
  ├── com.你的包名.activity
  |   ├── XXActivity.java  //在这里我们启动支付接口
  ├── com.你的包名.alipay
  |   ├── AlipayConfig.java  //支付的一些基础配置
  |   ├── Base64.java //RSA密钥转换
  |   ├── OrderInfoUtil2_0.java //构造订单的一些方法
  |   ├── PaymentHelper.java  //我把具体支付方法放在工具类里面
  |   └── PayResult.java  //支付结果
  |   └── PayReponse.java  //支付请求类
  |   └── SignUtils.java  //RSA签名类
  └── ...

具体类代码:

6

代码图.png

如果仔细看上面的工程结构图就知道,PaymentHelper是工具类,我把支付宝和微信支付都封装在一个工具类里面,这样可以不用每个页面都需要重写支付方法,很简单,很方便。

PaymentHelper.java:

/**
 * Created by zjp on 2017/12/21 10:46.
 * 支付宝和微信支付工具类
 */
public class PaymentHelper {
    private static final int SDK_PAY_FLAG = 1;
    private Activity mthis;
    private Map<String, String> result;

    /**
     * 支付宝支付
     */
    public void  startAliPay(Activity activity, PayReponse payReponse, String payRMB) {
        this.mthis = activity;
        if (activity == null || payReponse == null) {
            return;
        }
        if (TextUtils.isEmpty(AlipayConfig.PARTNER) || TextUtils.isEmpty(AlipayConfig.RSA2_PRIVATE) || TextUtils.isEmpty(AlipayConfig.SELLER)) {
            return;
        }

        /**
         * 这里只是为了方便直接向商户展示支付宝的整个支付流程;所以Demo中加签过程直接放在客户端完成;
         * 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成;
         * 防止商户私密数据泄露,造成不必要的资金损失,及面临各种安全风险;
         * 点击支付按钮出现的错误码,请查看:https://tech.open.alipay.com/support/knowledge/index.htm?categoryId=24120&scrollcheck=1#/?_k=d783mj
         * orderInfo的获取必须来自服务端;
         */
        boolean rsa2 = (AlipayConfig.RSA2_PRIVATE.length() > 0); 
        Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(AlipayConfig.APPID, rsa2, payRMB);
        String orderParam = OrderInfoUtil2_0.buildOrderParam(params); //拼接订单信息

        String privateKey = rsa2 ? AlipayConfig.RSA2_PRIVATE : AlipayConfig.RSA_PRIVATE;
        String sign = OrderInfoUtil2_0.getSign(params, privateKey, rsa2); //然后并对订单信息使用私钥进行RSA加密
        String orderInfo = orderParam + "&" + sign;
        new AliPayThread(orderInfo).start();  //支付行为需要在独立的非ui线程中执行
    }

    /**
     * 支付宝支付异步任务
     */
    private class AliPayThread extends Thread {
        private String orderInfo;
        private AliPayThread(String orderInfo) {
            this.orderInfo = orderInfo;
        }

        @Override
        public void run() {
            PayTask alipay = new PayTask(mthis);
            result = alipay.payV2(orderInfo, true);
            Log.i("zjp", "result=" + result.toString());
            Message msg = new Message();
            msg.what = SDK_PAY_FLAG;
            msg.obj = result;
            /**
              *  官方result返回结果参考:https://docs.open.alipay.com/204/105302
              *  我这里返回到result格式为:
              *  {
                    resultStatus = 9000, 
                    result = {
                       "alipay_trade_app_pay_response": {
                           "code": "10000",
                           "msg": "Success",
                           "app_id": "2017112400138529",
                           "auth_app_id": "2017112400138529",
                           "charset": "utf-8",
                           "timestamp": "2018-01-29 14:46:33",
                           "total_amount": "0.01",
                           "trade_no": "2018012921001004940219217398",
                           "seller_id": "2088821472668202",
                           "out_trade_no": "0129144616-2725"
                        }
                   }
               }
               */
            Log.d("zjp", "result=" + result);
            mHandler.sendMessage(msg);
        }
    }

    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) { 
            switch (msg.what) {
                case SDK_PAY_FLAG:
                    // 支付宝返回此次支付结果及加签,建议对支付宝签名信息拿签约时支付宝提供的公钥做验签
                    Map<String, String> mapPayResult = (Map<String, String>) msg.obj;
                    String resultStatus = mapPayResult.get("resultStatus");
                    // 判断resultStatus 为“9000”则代表支付成功,具体状态码代表参考:https://docs.open.alipay.com/204/105301
                    if (TextUtils.equals(resultStatus, "9000")) {
                        showToast("支付成功");
                        EventBus.getDefault().post(new PayResultEvent());//支付成功后,发个通知
                    } else {
                        // 判断resultStatus 为非“9000”则代表可能支付失败
                        // “8000”代表支付结果因为支付渠道原因或者系统原因还在等待支付结果确认,最终交易是否成功以服务端异步通知为准(小概率状态)     
                        if (TextUtils.equals(resultStatus, "8000")) {
                            showToast("支付结果确认中");
                        } else if (TextUtils.equals(resultStatus, "6001")) { //用户中途取消
                            showToast("取消支付");
                        } else {
                            // 其他值就可以判断为支付失败
                            showToast("支付失败");
                        }
                    }
                    break;
            }
        }
    }

OrderInfoUtil2_0.java:

/**
 * Created by zjp on 2017/12/19 15:13.
 */
public class OrderInfoUtil2_0 {
    /**
        * 构造支付订单参数列表
        */
    public static Map<String, String> buildOrderParamMap(String app_id, boolean rsa2, String price) {
        Map<String, String> keyValues = new HashMap<String, String>();
        keyValues.put("app_id", app_id);
        keyValues.put("biz_content", bizCotent(price));
        keyValues.put("charset", "utf-8");
        keyValues.put("method", "alipay.trade.app.pay");
        keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA");
        keyValues.put("timestamp", "2016-07-29 16:55:53");
        keyValues.put("version", "1.0");
        return keyValues;
    }

    public static String bizCotent(String price) {
        // 设置未付款交易的超时时间
        // 默认30分钟,一旦超时,该笔交易就会自动被关闭。
        // 取值范围:1m~15d。
        // m-分钟,h-小时,d-天,1c-当天(无论交易何时创建,都在0点关闭)。
        // 该参数数值不接受小数点,如1.5h,可转换为90m。
        String bizValue = "{"timeout_express":"15m",";
        bizValue += ""product_code":"QUICK_MSECURITY_PAY",";
        // 商品金额
        bizValue += ""total_amount":" + """ + price + "",";
        // 商品名称
        bizValue += ""subject":" + """ + "兔泊哥停取车交付款" + "",";
        // 商品详情
        bizValue += ""body":" + """ + "兔泊哥停取车付款界面" + "",";
        //商户网站唯一订单号
        bizValue += ""out_trade_no":" + """ + getOutTradeNo() + ""}";
        return bizValue;
    }

    /**
      * 要求外部订单号必须唯一。
      */
    public static String getOutTradeNo() {
        SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault());
        Date date = new Date();
        String key = format.format(date);

        Random r = new Random();
        key = key + r.nextInt();
        key = key.substring(0, 15);
        return key;
    }

}

AlipayConfig.java:

/**
  * Created by zjp on 2017/12/19 15:11.
  */
public class AlipayConfig {
    /**
     * 支付宝支付业务:入参app_id,上文创建应用时候,已经得到
     * 由于App支付功能需要签约,因此需要上传公司信息和证件等资料进行签约
     * 以下参数,由上传完整公司信息后即可得到
     */
    public static final String APPID = "XXXXXXXXXXXXX";
    // 商户PID
    public static final String PARTNER = "XXXXXXXXXXXXX";

    // 商户收款账号

    public static final String SELLER = "XXXXXXXXXX";
    /**
        * 支付宝账户登录授权业务:入参target_id值
        * 可以用时间戳
        */
    public static final String TARGET_ID = OrderInfoUtil2_0.getOutTradeNo();

    /** 商户私钥,pkcs8格式 */
    /** 如下私钥,RSA2_PRIVATE 或者 RSA_PRIVATE 只需要填入一个 */
    /** 如果商户两个都设置了,优先使用 RSA2_PRIVATE */
    /** RSA2_PRIVATE 可以保证商户交易在更加安全的环境下进行,建议使用 RSA2_PRIVATE */
    /** 获取 RSA2_PRIVATE,建议使用支付宝提供的公私钥生成工具生成, */
    /**
     *  使用支付宝提供的工具生成RSA公钥和私钥
     *  工具地址:https://doc.open.alipay.com/docs/doc.htmtreeId=291&articleId=106097&docType=1
     */
    public static final String RSA2_PRIVATE = "XXXXXXXXX";
    public static final String RSA_PRIVATE = "";
}

至此,支付宝支付代码已完成!

希望可以帮助大家 如果哪里有什么不对或者不足的地方,还望读者多多提意见或建议!