Android版-支付寶APP支付

此項(xiàng)目已開源 趕快來(lái)圍觀 Start支持下吧
客戶端開源地址-JPay】【服務(wù)端端開源地址-在com.javen.alipay
包名下

上一篇詳細(xì)介紹了微信APP支付 點(diǎn)擊這里

此篇文章來(lái)詳細(xì)介紹下支付寶APP支付

目錄
1、支付寶與微信對(duì)比(申請(qǐng)、費(fèi)率、結(jié)算周期)
2、支付寶上線應(yīng)用
3、支付寶App支付Android集成流程詳解
4、服務(wù)端實(shí)現(xiàn)(建議直接官方提供的服務(wù)端SDK)
5、客戶端實(shí)現(xiàn)
6、常見錯(cuò)誤解決方案(ALI40247、AL38173)

補(bǔ)充(20170513) 支付寶APP支付可以使用沙箱環(huán)境測(cè)試。如需開啟測(cè)試模式只需要在OnCreate中添加如下代碼。沙箱環(huán)境測(cè)試APP支付中請(qǐng)使用沙箱版錢包測(cè)試:點(diǎn)擊開發(fā)者中心-沙箱環(huán)境-沙箱工具

EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
使用沙箱環(huán)境測(cè)試

1、支付寶與微信對(duì)比

申請(qǐng):【微信開發(fā)平臺(tái)】注冊(cè)的郵箱不能與騰訊其他產(chǎn)品同號(hào),需要開發(fā)者資質(zhì)認(rèn)證(需要¥300,而且支付商戶與公眾號(hào)不公用)而支付寶只需要企業(yè)實(shí)名認(rèn)證,其他質(zhì)料類似(營(yíng)業(yè)執(zhí)照、組織機(jī)構(gòu)代碼(現(xiàn)在都三證合一了))。

費(fèi)率:
微信簽約匯率參考質(zhì)料】【支付寶簽約匯率參考質(zhì)料

結(jié)算周期:
微信結(jié)算周期: 話費(fèi)通訊、財(cái)經(jīng)資訊、股票軟件類、網(wǎng)絡(luò)虛擬服務(wù)以及互聯(lián)網(wǎng)相關(guān)行業(yè) T+7 眾籌 T+3 其他T+1,結(jié)算款項(xiàng)自動(dòng)提現(xiàn)到商戶的結(jié)算賬戶,一般從結(jié)算日起3個(gè)工作日到賬(具體到賬時(shí)間視不同銀行到賬情況而定)

支付寶結(jié)算周期:及時(shí)到支付寶賬號(hào)

2、支付寶上線應(yīng)用

1、注冊(cè)賬號(hào)并實(shí)名認(rèn)證

注冊(cè)賬號(hào)了沒(méi)有實(shí)名認(rèn)證進(jìn)入開發(fā)平臺(tái)的管理中心會(huì)提示如下圖

實(shí)名認(rèn)證

為什么要實(shí)名認(rèn)證

賬戶認(rèn)證-商家認(rèn)證流程介紹

商家認(rèn)證流程
2、創(chuàng)建應(yīng)用開通支付并上線

官方文檔有詳細(xì)的介紹-創(chuàng)建應(yīng)用、配置應(yīng)用、上線應(yīng)用

資源下載:App支付DEMO&SDK、生成與配置密鑰

資源下載匯總
簽名工具

注意簽名工具目錄不能包含中文

簽名工具的使用
上傳密鑰

這里上傳的是rsa_public_key.pem Java版簽名使用的密鑰是rsa_private_key_pkcs8.pem

3、支付寶App支付Android集成流程詳解

官方文檔 點(diǎn)擊這里

1、導(dǎo)入支付寶SDK 其中SDK在圖資源下載匯總WS.APP_PAY_SDK_BASE_20.zip

2、修改Manifest

在商戶應(yīng)用工程的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、權(quán)限聲明

<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、如需混洗、添加混淆規(guī)則

在商戶應(yīng)用工程的proguard-project.txt里添加以下相關(guān)規(guī)則:
alipaySDK-xxxxx.jar根據(jù)下載的做響應(yīng)的修改

-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、支付接口調(diào)用

獲取PayTask支付對(duì)象調(diào)用支付(支付行為需要在獨(dú)立的非ui線程中執(zhí)行
具體實(shí)現(xiàn)參考客戶端實(shí)現(xiàn),調(diào)用支付需要使用到orderInfo 具體實(shí)現(xiàn)參考服務(wù)端實(shí)現(xiàn)。

6、支付結(jié)果獲取和處理

調(diào)用pay方法支付后,將通過(guò)2種途徑獲得支付結(jié)果:

1、同步返回 商戶應(yīng)用客戶端通過(guò)當(dāng)前調(diào)用支付的Activity的Handler對(duì)象,通過(guò)它的回調(diào)函數(shù)獲取支付結(jié)果。
2、異步通知 商戶需要提供一個(gè)http協(xié)議的接口,包含在請(qǐng)求支付的入?yún)⒅?,其key對(duì)應(yīng)notify_url。支付寶服務(wù)器在支付完成后,會(huì)以POST方式調(diào)用notify_url傳輸數(shù)據(jù)。

4、服務(wù)端實(shí)現(xiàn)

代碼來(lái)自Demo 并做簡(jiǎn)單的封裝

具體實(shí)現(xiàn)截圖

設(shè)置支付寶業(yè)務(wù)參數(shù)

設(shè)置支付寶業(yè)務(wù)參數(shù)

注意: 支付寶App支付不支持沙盒模式(此坑糾結(jié)了很長(zhǎng)時(shí)間),使用沙盒模式會(huì)出現(xiàn)ALI40247 錯(cuò)誤,文末有錯(cuò)誤的詳細(xì)說(shuō)明。

核心實(shí)現(xiàn)代碼如下

/**
     * App支付
     */
    public void appPay(){
        String orderInfo;
        try {
            
            String body="我是測(cè)試數(shù)據(jù)";
            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");
        }
    }

具體實(shí)現(xiàn)參考【開源項(xiàng)目

補(bǔ)充 使用支付服務(wù)端SDK實(shí)現(xiàn)獲取預(yù)付訂單

1、先獲取到AlipayClient

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

2、使用AlipayTradeAppPayRequest封裝請(qǐng)求

/**
     * 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{
        //實(shí)例化具體API對(duì)應(yīng)的request類,類名稱和接口名稱對(duì)應(yīng),當(dāng)前調(diào)用接口名稱:alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已經(jīng)封裝掉了公共參數(shù),這里只需要傳入業(yè)務(wù)參數(shù)。以下方法為sdk的model入?yún)⒎绞?model和biz_content同時(shí)存在的情況下取biz_content)。
        request.setBizModel(model);
        request.setNotifyUrl(notifyUrl);
        //這里和普通的接口調(diào)用不同,使用的是sdkExecute
        AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
        return response;
    }

3、控制器封裝數(shù)據(jù)給客戶端

/**
     * app支付
     */
    public void appPay(){
        try {
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
            model.setBody("我是測(cè)試數(shù)據(jù)");
            model.setSubject("App支付測(cè)試-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");
        }
    }

服務(wù)端異步通知

/**
     * App支付支付回調(diào)通知
     * https://doc.open.alipay.com/docs/doc.htm?treeId=54&articleId=106370&
     * docType=1#s3
     */
    public void app_pay_notify() {
        try {
            // 獲取支付寶POST過(guò)來(lái)反饋信息
            Map<String, String> params = AliPayApi.toMap(getRequest());
            for (Map.Entry<String, String> entry : params.entrySet()) {
                System.out.println(entry.getKey() + " = " + entry.getValue());
            }
            // 切記alipaypublickey是支付寶的公鑰,請(qǐng)去open.alipay.com對(duì)應(yīng)應(yīng)用下查看。
            // 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");
        }
    }

將異步通知的參數(shù)轉(zhuǎn)化為Map

/**
     * 將異步通知的參數(shù)轉(zhuǎn)化為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] + ",";
            }
            // 亂碼解決,這段代碼在出現(xiàn)亂碼時(shí)使用。
            // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        return params;
    }

5、客戶端實(shí)現(xiàn)

客戶端請(qǐng)求此action返回orderInfo,默認(rèn)的訪問(wèn)地址為http://[域名或者IP]:端口號(hào)/[項(xiàng)目名稱]/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, "正在調(diào)起支付", Toast.LENGTH_SHORT).show();
                    IPayLogic.getIntance(mContext).startAliPay(orderInfo);

                }else{

                    Log.d("PAY_GET", "返回錯(cuò)誤"+message);
//                  Toast.makeText(mContext, "返回錯(cuò)誤:"+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);
    }

}

支付接口調(diào)用

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();
     } 

客戶端通過(guò)回調(diào)函數(shù)獲取支付結(jié)果

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){ 
                //返回狀態(tài)以及詳細(xì)的描述參考 
                // 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, "支付寶測(cè)試", Toast.LENGTH_SHORT).show();
 
        Order order = new Order();
        order.setBody("會(huì)員充值中心");
        order.setParaTradeNo(System.currentTimeMillis()+"");
        order.setTotalFee(20);
        order.setAttach("json");//附加參數(shù)
        order.setNofityUrl("http://www.xxxx.com");//支付成功服務(wù)端回調(diào)通知的地址
 
        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("回調(diào)過(guò)來(lái)的狀態(tài)》"+alicode+" message>"+((message!=null && message.isEmpty())?"":message));
                Toast.makeText(MainActivity.this, "回調(diào)過(guò)來(lái)的狀態(tài)》" + alicode, Toast.LENGTH_SHORT).show();
            } 
        }); 
    } 

6、常見錯(cuò)誤解決方案

APP支付報(bào)錯(cuò)ALI40247處理方案

ALI40247的錯(cuò)誤
**有2種情況 1、沒(méi)有權(quán)限 2、簽名失敗 **

可以查看論壇提供的這個(gè)解決方案

AL38173的錯(cuò)誤一般是參數(shù)問(wèn)題造成的,建議檢查請(qǐng)求參數(shù),對(duì)照文檔查看,比如:參數(shù)少了、多了、亂碼、名稱不對(duì),還有必傳參數(shù)是否都請(qǐng)求提交給支付寶了等等.

如果在網(wǎng)上查閱質(zhì)料還未解決,可以尋求阿里的在線支持【支持中心

此項(xiàng)目已開源 趕快來(lái)圍觀 Start支持下吧

客戶端開源地址-JPay】【服務(wù)端端開源地址-在com.javen.alipay包名下

推薦閱讀
Android版-微信APP支付
極速開發(fā)微信公眾號(hào)之微信買單
極速開發(fā)微信公眾號(hào)之公眾號(hào)支付
極速開發(fā)微信公眾號(hào)之掃碼支付
極速開發(fā)微信公眾號(hào)之刷卡支付
極速開發(fā)微信公眾號(hào)之現(xiàn)金紅包

記錄學(xué)習(xí)的點(diǎn)滴,以此勉勵(lì)不斷奮斗的自己?????? 如果對(duì)你有幫助記得點(diǎn)喜歡告訴我哈

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容