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

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

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

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

调用支付支付宝支付1.gif

调用支付支付宝支付2.gif

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

步骤

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

开放平台首页.png

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

工程中的sdk.png

大家注意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签名类
  └── ...

具体类代码:


代码图.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 = "";
   }

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,448评论 25 707
  • 1、App支付简介 买家在手机、掌上电脑等无线设备的应用程序内,可通过支付宝进行付款购买特定服务或商品,资金即时到...
    PZcoder阅读 43,955评论 5 22
  • #复盘第47天# 健身运动,极限跳伞。我需要一种释放自我的机会。 读遍新潮温情哲理书,赏遍美景艺术人性。 学习是一...
    Ariel元阅读 131评论 0 0