Android版-支付宝APP支付

作者: Javen205 | 来源:发表于2016-12-08 23:09 被阅读7197次

    此项目已开源 赶快来围观 Start支持下吧
    客户端开源地址-JPay】【服务端端开源地址-在com.javen.alipay
    包名下

    上一篇详细介绍了微信APP支付 点击这里

    此篇文章来详细介绍下支付宝APP支付

    目录
    1、支付宝与微信对比(申请、费率、结算周期)
    2、支付宝上线应用
    3、支付宝App支付Android集成流程详解
    4、服务端实现(建议直接官方提供的服务端SDK)
    5、客户端实现
    6、常见错误解决方案(ALI40247、AL38173)

    补充(20170513) 支付宝APP支付可以使用沙箱环境测试。如需开启测试模式只需要在OnCreate中添加如下代码。沙箱环境测试APP支付中请使用沙箱版钱包测试:点击开发者中心-沙箱环境-沙箱工具

    EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
    
    使用沙箱环境测试

    1、支付宝与微信对比

    申请:【微信开发平台】注册的邮箱不能与腾讯其他产品同号,需要开发者资质认证(需要¥300,而且支付商户与公众号不公用)而支付宝只需要企业实名认证,其他质料类似(营业执照、组织机构代码(现在都三证合一了))。

    费率:
    微信签约汇率参考质料】【支付宝签约汇率参考质料

    结算周期:
    微信结算周期: 话费通讯、财经资讯、股票软件类、网络虚拟服务以及互联网相关行业 T+7 众筹 T+3 其他T+1,结算款项自动提现到商户的结算账户,一般从结算日起3个工作日到账(具体到账时间视不同银行到账情况而定)

    支付宝结算周期:及时到支付宝账号

    2、支付宝上线应用

    1、注册账号并实名认证

    注册账号了没有实名认证进入开发平台的管理中心会提示如下图

    实名认证
    为什么要实名认证

    账户认证-商家认证流程介绍

    商家认证流程
    2、创建应用开通支付并上线

    官方文档有详细的介绍-创建应用、配置应用、上线应用

    资源下载:App支付DEMO&SDK生成与配置密钥

    资源下载汇总 签名工具

    注意签名工具目录不能包含中文

    签名工具的使用 上传密钥

    这里上传的是rsa_public_key.pem Java版签名使用的密钥是rsa_private_key_pkcs8.pem

    3、支付宝App支付Android集成流程详解

    官方文档 点击这里

    1、导入支付宝SDK 其中SDK在图资源下载汇总WS.APP_PAY_SDK_BASE_20.zip

    2、修改Manifest

    在商户应用工程的AndroidManifest.xml文件里面添加声明:

    <activity
                android:name="com.alipay.sdk.app.H5PayActivity"
                android:configChanges="orientation|keyboardHidden|navigation"
                android:exported="false"
                android:screenOrientation="behind" >
    </activity>
    <activity
                android:name="com.alipay.sdk.auth.AuthActivity"
                android:configChanges="orientation|keyboardHidden|navigation"
                android:exported="false"
                android:screenOrientation="behind" >
     </activity>
    
    

    3、权限声明

    <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" />
    
    

    4、如需混洗、添加混淆规则

    在商户应用工程的proguard-project.txt里添加以下相关规则:
    alipaySDK-xxxxx.jar根据下载的做响应的修改

    -libraryjars libs/alipaySDK-xxxxx.jar
     
    -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 *;}
    
    

    5、支付接口调用

    获取PayTask支付对象调用支付(支付行为需要在独立的非ui线程中执行
    具体实现参考客户端实现,调用支付需要使用到orderInfo 具体实现参考服务端实现

    6、支付结果获取和处理

    调用pay方法支付后,将通过2种途径获得支付结果:

    1、同步返回 商户应用客户端通过当前调用支付的Activity的Handler对象,通过它的回调函数获取支付结果。
    2、异步通知 商户需要提供一个http协议的接口,包含在请求支付的入参中,其key对应notify_url。支付宝服务器在支付完成后,会以POST方式调用notify_url传输数据。

    4、服务端实现

    代码来自Demo 并做简单的封装

    具体实现截图

    设置支付宝业务参数

    设置支付宝业务参数

    注意: 支付宝App支付不支持沙盒模式(此坑纠结了很长时间),使用沙盒模式会出现ALI40247 错误,文末有错误的详细说明。

    核心实现代码如下

    /**
         * App支付
         */
        public void appPay(){
            String orderInfo;
            try {
                
                String body="我是测试数据";
                String passback_params="123";
                String subject="1";
                String total_amount="0.01";
                String notify_url="http://javen.ittun.com/alipay/pay_notify";
                
                String appId;
                String rsa_private;
                if (isDebug) {
                    appId=prop.get("test_appId").trim();
                    rsa_private=prop.get("test_rsa_private").trim();
    System.out.println("test。。。。");
                }else {
                    appId=prop.get("appId").trim();
                    rsa_private=prop.get("rsa_private").trim();
                }
    System.out.println("appId:"+appId);         
    System.out.println("rsa_private:"+rsa_private);         
                
                BizContent content = new BizContent();
                content.setBody(body);
                content.setOut_trade_no(OrderInfoUtil2_0.getOutTradeNo());;
                
                content.setPassback_params(passback_params);
                
                content.setSubject(subject);
                
                content.setTotal_amount(total_amount);
                content.setProduct_code("QUICK_MSECURITY_PAY");
                
                
                Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(appId,notify_url,content);
                String orderParam = OrderInfoUtil2_0.buildOrderParam(params);
                String sign = OrderInfoUtil2_0.getSign(params, rsa_private);
                orderInfo = orderParam + "&" + sign;
                log.info("orderInfo>"+orderInfo);
                result.success(orderInfo);
                renderJson(result);
                
            } catch (Exception e) {
                e.printStackTrace();
                result.addError("system error");
            }
        }
    

    具体实现参考【开源项目

    补充 使用支付服务端SDK实现获取预付订单

    1、先获取到AlipayClient

            alipayClient = new DefaultAlipayClient(serverUrl, appId, privateKey, format, charset, alipayPulicKey, signType);
    
    

    2、使用AlipayTradeAppPayRequest封装请求

    /**
         * App支付
         * @param model
         * @param notifyUrl 
         * @return
         * @throws AlipayApiException
         */
        public static String startAppPayStr(AlipayTradeAppPayModel model, String notifyUrl) throws AlipayApiException{
            AlipayTradeAppPayResponse response = appPay(model,notifyUrl);
            return response.getBody();
        }
        
        /**
         * App 支付
         * https://doc.open.alipay.com/docs/doc.htm?treeId=54&articleId=106370&docType=1
         * @param model
         * @param notifyUrl 
         * @return
         * @throws AlipayApiException
         */
        public static AlipayTradeAppPayResponse appPay(AlipayTradeAppPayModel model, String notifyUrl) throws AlipayApiException{
            //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
            //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
            request.setBizModel(model);
            request.setNotifyUrl(notifyUrl);
            //这里和普通的接口调用不同,使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            return response;
        }
    

    3、控制器封装数据给客户端

    /**
         * app支付
         */
        public void appPay(){
            try {
                AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
                model.setBody("我是测试数据");
                model.setSubject("App支付测试-By Javen");
                model.setOutTradeNo(StringUtils.getOutTradeNo());
                model.setTimeoutExpress("30m");
                model.setTotalAmount("0.01");
                model.setPassbackParams("callback params");
                model.setProductCode("QUICK_MSECURITY_PAY");
                String orderInfo = AliPayApi.startAppPayStr(model,AliPayApi.notify_domain+"/alipay/app_pay_notify");
                result.success(orderInfo);
                renderJson(result);
                
            } catch (AlipayApiException e) {
                e.printStackTrace();
                result.addError("system error");
            }
        }
    
    

    服务端异步通知

    /**
         * App支付支付回调通知
         * https://doc.open.alipay.com/docs/doc.htm?treeId=54&articleId=106370&
         * docType=1#s3
         */
        public void app_pay_notify() {
            try {
                // 获取支付宝POST过来反馈信息
                Map<String, String> params = AliPayApi.toMap(getRequest());
                for (Map.Entry<String, String> entry : params.entrySet()) {
                    System.out.println(entry.getKey() + " = " + entry.getValue());
                }
                // 切记alipaypublickey是支付宝的公钥,请去open.alipay.com对应应用下查看。
                // boolean AlipaySignature.rsaCheckV1(Map<String, String> params,
                // String publicKey, String charset, String sign_type)
                boolean flag = AlipaySignature.rsaCheckV1(params, AliPayApi.ALIPAY_PUBLIC_KEY, AliPayApi.CHARSET,
                        AliPayApi.SIGN_TYPE);
                if (flag) {
                    // TODO
                    System.out.println("success");
                    renderText("success");
                    return;
                } else {
                    // TODO
                    System.out.println("failure");
                    renderText("failure");
                }
            } catch (AlipayApiException e) {
                e.printStackTrace();
                renderText("failure");
            }
        }
    

    将异步通知的参数转化为Map

    /**
         * 将异步通知的参数转化为Map
         * @param request
         * @return
         */
        public static Map<String, String> toMap(HttpServletRequest request) {
            System.out.println(">>>>" + request.getQueryString());
            Map<String, String> params = new HashMap<String, String>();
            Map<String, String[]> requestParams = request.getParameterMap();
            for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
                String name = (String) iter.next();
                String[] values = (String[]) requestParams.get(name);
                String valueStr = "";
                for (int i = 0; i < values.length; i++) {
                    valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
                }
                // 乱码解决,这段代码在出现乱码时使用。
                // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
                params.put(name, valueStr);
            }
            return params;
        }
    

    5、客户端实现

    客户端请求此action返回orderInfo,默认的访问地址为http://[域名或者IP]:端口号/[项目名称]/alipay/appPay

    成功将返回:

    {
      "code": 0,
      "data": "charset=utf-8&biz_content=%7B%22out_trade_no%22%3A%22120822453414812%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%221%22%2C%22body%22%3A%22%E6%88%91%E6%98%AF%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22passback_params%22%3A%22123%22%7D&method=alipay.trade.app.pay&format=json&notify_url=http%3A%2F%2Fjaven.ittun.com%2Falipay%2Fpay_notify&app_id=2016102000727659&sign_type=RSA&version=1.0&timestamp=2016-12-08+22%3A45%3A34&sign=m6fpNI58jkOIHROtm8Q2V1Ei7bxXc14JHtJEeqGM0B8dWlq3d%2FfAqpoQTZgeBb%2FVK%2B%2BydOJzVvSWc89dBxhYUq72KOeUwHiKlRJBFMnEMAZbJlRxqu9%2BRX2q7HSRmA6WRg75O68HZhkIhtO3bSNx3s710tMHmQCN230JoVWiwHw%3D",
      "message": null
    }
    
    /** 
     * 获取支付宝App支付订单信息 
     * @return 
     */ 
     public String getAliPayOrderInfo(Order order){
     String result=HttpKit.get(Constants.ALIPAY_URL);
     return result;
     } 
    

    异步获取orderInfo

    public class AliPay extends AsyncTask<Object, Integer, String> {
        private Activity mContext;
        public AliPay(Activity context) {
            this.mContext = context;
        }
        
        @Override
        protected String doInBackground(Object... params) {
            return  IPayLogic.getIntance(mContext).getAliPayOrderInfo((Order)params[0]);
        }
        
        @Override
        protected void onPostExecute(String result) {
            try {
                if (result!=null) {
                    System.out.println("AliPay result>"+result);
                    JSONObject data = new JSONObject(result);
                    String message = data.getString("message");
                    int code = data.getInt("code");
    
                    if(code == 0){
                        String orderInfo = data.getString("data");
    
                        Toast.makeText(mContext, "正在调起支付", Toast.LENGTH_SHORT).show();
                        IPayLogic.getIntance(mContext).startAliPay(orderInfo);
    
                    }else{
    
                        Log.d("PAY_GET", "返回错误"+message);
    //                  Toast.makeText(mContext, "返回错误:"+message, Toast.LENGTH_SHORT).show();
                    }
                }else {
                    System.out.println("get  AliPay exception, is null");
                }
            } catch (Exception e) {
                Log.e("PAY_GET", "异常:"+e.getMessage());
                Toast.makeText(mContext, "异常:"+e.getMessage(), Toast.LENGTH_SHORT).show();
            }
            super.onPostExecute(result);
        }
    
    }
    

    支付接口调用

    public void startAliPay(final String orderInfo){
             Runnable payRunnable = new Runnable() {
                 @Override 
                 public void run() { 
                     PayTask alipay = new PayTask(mContext);
                     Map<String, String> result = alipay.payV2(orderInfo, true);
                     Message msg = new Message();
                     msg.obj = result;
                     mHandler.sendMessage(msg);
                 } 
             }; 
             Thread payThread = new Thread(payRunnable);
             payThread.start();
         } 
    

    客户端通过回调函数获取支付结果

    private Handler mHandler = new Handler(Looper.getMainLooper()) {
            @SuppressWarnings("unchecked") 
            public void handleMessage(Message msg) { 
                PayResult payResult = new PayResult((Map<String, String>) msg.obj); 
                System.out.println("alipay call "+payResult.toString()); 
                String resultStatus = payResult.getResultStatus(); 
                String memo = payResult.getMemo(); 
                if (Constants.payListener !=null){ 
                    //返回状态以及详细的描述参考 
                    // https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.xN1NnL&treeId=204&articleId=105302&docType=1 
                    Constants.payListener.onPay(-2,resultStatus,memo); 
                } 
            } 
        }; 
    

    客户端具体使用方法

     public void testAliPay(View view){
            Toast.makeText(this, "支付宝测试", Toast.LENGTH_SHORT).show();
     
            Order order = new Order();
            order.setBody("会员充值中心");
            order.setParaTradeNo(System.currentTimeMillis()+"");
            order.setTotalFee(20);
            order.setAttach("json");//附加参数
            order.setNofityUrl("http://www.xxxx.com");//支付成功服务端回调通知的地址
     
            IPay.getIntance(MainActivity.this).toPay(IPay.PayMode.ALIPAY,order,new IPay.IPayListener() {
     
                @Override 
                public void onPay(int wxcode, String alicode, String message) {
                    System.out.println("回调过来的状态》"+alicode+" message>"+((message!=null && message.isEmpty())?"":message));
                    Toast.makeText(MainActivity.this, "回调过来的状态》" + alicode, Toast.LENGTH_SHORT).show();
                } 
            }); 
        } 
    

    6、常见错误解决方案

    APP支付报错ALI40247处理方案

    ALI40247的错误
    **有2种情况 1、没有权限 2、签名失败 **

    可以查看论坛提供的这个解决方案

    AL38173的错误一般是参数问题造成的,建议检查请求参数,对照文档查看,比如:参数少了、多了、乱码、名称不对,还有必传参数是否都请求提交给支付宝了等等.

    如果在网上查阅质料还未解决,可以寻求阿里的在线支持【支持中心

    此项目已开源 赶快来围观 Start支持下吧

    客户端开源地址-JPay】【服务端端开源地址-在com.javen.alipay包名下

    推荐阅读
    Android版-微信APP支付
    极速开发微信公众号之微信买单
    极速开发微信公众号之公众号支付
    极速开发微信公众号之扫码支付
    极速开发微信公众号之刷卡支付
    极速开发微信公众号之现金红包

    记录学习的点滴,以此勉励不断奋斗的自己✌️✌️✌️ 如果对你有帮助记得点喜欢告诉我哈

    相关文章

      网友评论

      • Samuel0827:开源项目JPay法运行~
        Javen205:@11e1ee052f3c wxkey.jks 这个签名的密钥我没有上传 你修改一下签名的配置就可以了
        Samuel0827:@Javen205 logcat没有报错信息,messages里有很多error;我提取了两条可能有用的错误信息:
        Error:warning: Ignoring InnerClasses attribute for an anonymous inner class

        Error:Execution failed for task ':app:validateSigningBaiduDebug'.
        > Keystore file C:\Users\Administrator\Desktop\JPay-master\app\wxkey.jks not found for signing config 'debugConfig'.
        Javen205:@11e1ee052f3c 无法运行? 有异常日志吗?
      • 小麦子一号:可以研究下ping++支付么
        Javen205:@0b9790fa85f0 @小麦子一号 我注册了一个账号 提示需要企业认证 现在个人使用不了?
        0b9790fa85f0:@小麦子一号 ping++收费的 虽然说对开发者很便利 但是对公司就很不划算了
        Javen205:@小麦子一号 ping++ 这个是第三方支付商吧 接入应该比原生的简单 有时间我看看

      本文标题:Android版-支付宝APP支付

      本文链接:https://www.haomeiwen.com/subject/wnujmttx.html