1. 程式人生 > >Unity Android平臺接入支付寶

Unity Android平臺接入支付寶

配置

建立應用,配置應用參考上面的給的官網文件。

支付寶SDK

下載SDK&Demo。解壓之後會有兩個資料夾:alipay_demo和alipay_sdk,別急後面會用到。

建立android庫工程

這裡用AndroidStudio來建立Android工程,不用Eclipse了,配環境好蛋疼。。。

1.

建立一個新的Android工程,Package Name要和Unity -> PlayerSettings裡的Bundle Identifier保持一致。

2.

Minimum API Level就直接預設得了,大部分通用

3.

選擇Activity的模版,我們選擇Empty Activity。因為不需要編寫Android原生介面。

4.

然後是設定Activity的名字介面,因為我們不需要程式設計Android介面,所以取消勾選Generate Layout File選項。不取消也可以,可以建立工程後手動刪除Layout資料夾。

5.

建立工作完成,下面對照著下圖刪除沒用東西。

只保留了兩個檔案—— AndroidManifest.xml和MainActivity

6.

修改AndroidManifest,修改後如下:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.xxl.youxigame">

        <application
            android:allowBackup="true"
            android:supportsRtl="true">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
                <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
            </activity>
        </application>

    </manifest>

7.

開啟Gradle Scripts(Gradle是一種安卓構建指令碼)下的build.gradle(Moudle:app),修改如下(這是因為庫檔案不能設定applcation Id,另外build.gradle會包含單元測試的配置,也需要刪除):

        //apply plugin: 'com.android.application'
        apply plugin: 'com.android.library'

        android {
            compileSdkVersion 24
            buildToolsVersion "24.0.2"

            defaultConfig {
                minSdkVersion 15
                targetSdkVersion 24
                versionCode 1
                versionName "1.0"
            }
            buildTypes {
                release {
                    minifyEnabled false
                    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                }
            }
        }

基本環境配置完畢,接下來就是接入支付寶SDK了。

接入支付寶

首先我們需要引入支付寶SDK的jar包,將alipay_sdk下的alipaySdk-20XXXXXX.jar和Unity提供的jar包拷貝到專案路徑/app/libs資料夾下,

    Unity提供的jar包路徑是:
    Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes\classes.jar
    如果你的專案採用il2cpp編譯,那麼路徑則是:
    Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Release\Classes\classes.jar

然後回到AndroidStudio,選單欄選擇File -> Project Structure介面,點選app -> Dependencies如所示:

點選旁邊的減號,刪掉目前這三個以來庫,然後點選加號 -> Jar Dependency,選擇剛才拷貝到Libs下面的那兩個jar檔案,點選確定。可以發現build.gradle(Moudle:app)下depedencies裡出現了支付寶的Sdk jar包,完整的build.gradle如下:

        //apply plugin: 'com.android.application'
        apply plugin: 'com.android.library'

        android {
            compileSdkVersion 24
            buildToolsVersion "24.0.2"

            defaultConfig {
                minSdkVersion 15
                targetSdkVersion 24
                versionCode 1
                versionName "1.0"
            }
            buildTypes {
                release {
                    minifyEnabled false
                    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                }
            }
        }

        dependencies {
            compile files('libs/alipaySdk-20170710.jar')
            compile files('libs/classes.jar')
        }

到這裡後,我們先來測試一下配置是否正確,再進行下面的步驟。
點選Android Studio左下角的Build Variants,如圖:

將debug修改為release:

最後點選選單欄Build -> Build APK,稍等,如果出現Build Successfully的提示,說明一切正常。如圖:

點選Show in Explorer,選擇outputs -> aar,會發現一個app-release.aar。這就是build生成的檔案,aar可以視為jar的升級版。其實它們都只是一個zip檔案。

    注意:
    生成的aar檔案裡,我們需要刪除libs/class.jar!將aar檔案字尾改為zip,使用壓縮軟體刪除即可。因為Unity在打包時,會將自帶的那個classes.jar拷貝進apk,如果aar裡的classes.jar不刪除,打包時就會產出衝突,得到下面的錯誤:
    IOException: Failed to Move File / Directory from 'Temp/StagingArea\android-libraries\app-release\classes.jar' to 'Temp/StagingArea\android-libraries\app-release\libs\classes.jar'.
    也就是說,每次我們測試後,都需要將aar裡的這個jar包手動刪除。

封裝支付寶支付介面

把alipay_demo下檔案直接拖到androidstudio工程中,需要如圖這些檔案:

對就是AuthResult、Base64、OrderInfoUtil2_0、PayResult、SignUtils這五個類即可,MainActivity建立時就有。

我們需要做的就是修改MainActivity這個類,其他都是工具定義相關的類,看下官方提供的註釋就行。直接上程式碼了:

        package com.xxl.youxigame;

        import android.annotation.SuppressLint;
        import android.os.Bundle;
        import android.os.Handler;
        import android.os.Message;
        import android.text.TextUtils;
        import android.widget.Toast;

        import com.unity3d.player.*;
        import com.alipay.sdk.app.PayTask;

        import java.util.Map;

        public class MainActivity extends UnityPlayerActivity {

            private static final  int SDK_PAY_FLAG=1;
            private static final int SDK_AUTH_FLAG = 2;
            private static final String RESULT_SUCCESS="9000";
            private static final String TIP_PAY_SUCCESS="支付成功";
            private static final String TIP_PAY_FAILED="支付失敗";

            // 支付結果回撥,僅作參考,以服務端確認為準!
            @SuppressLint("HandlerLeak")
            private Handler mHandler=new Handler(){
                @SuppressWarnings("unused")
                public void handleMessage(Message msg) {
                    switch (msg.what)
                    {
                        case SDK_PAY_FLAG:
                        {
                            @SuppressWarnings("unchecked")
                            PayResult payResult = new PayResult((Map<String, String>) msg.obj);
                            String resultInfo = payResult.getResult();
                            String resultStatus = payResult.getResultStatus();
                            if (TextUtils.equals(resultStatus, RESULT_SUCCESS))
                            {
                                Toast.makeText(MainActivity.this, TIP_PAY_SUCCESS, Toast.LENGTH_SHORT).show();
                            } else
                            {
                                Toast.makeText(MainActivity.this, TIP_PAY_FAILED, Toast.LENGTH_SHORT).show();
                            }
                            break;
                        }
                        case SDK_AUTH_FLAG: {
                            @SuppressWarnings("unchecked")
                            AuthResult authResult = new AuthResult((Map<String, String>) msg.obj, true);
                            String resultStatus = authResult.getResultStatus();

                            // 判斷resultStatus 為“9000”且result_code
                            // 為“200”則代表授權成功,具體狀態碼代表含義可參考授權介面文件
                            if (TextUtils.equals(resultStatus, "9000") && TextUtils.equals(authResult.getResultCode(), "200")) {
                                // 獲取alipay_open_id,調支付時作為引數extern_token 的value
                                // 傳入,則支付賬戶為該授權賬戶
                                Toast.makeText(MainActivity.this,
                                        "授權成功\n" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT)
                                        .show();
                            } else {
                                // 其他狀態值則為授權失敗
                                Toast.makeText(MainActivity.this,
                                        "授權失敗" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT).show();

                            }
                            break;
                        }
                        default:
                            break;
                    }
                }
            };

              //這些引數根據自己實際情況填入,參見第一步配置中的官網文件連結
            /** 支付寶支付業務:入參app_id */
            public static final String APPID = "2017061500021222";
            /** 支付寶賬戶登入授權業務:入參pid值 */
            public static final String PID = "2088102123816631";
            /** 支付寶賬戶登入授權業務:入參target_id值 */
            public static final String TARGET_ID = "kkkkk091125";
             //測試用的私鑰
            public static final String RSA2_PRIVATE = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJkhXBA/IH5uU8KcIw4p5x4i0zu+LZVAnff/z3Fd/JGc/iKrIFMJ+Gdkzd9lc9pM3pEkb8Hr7zuNb4YAZOYwxUJGt18IIhsr/DImCd+4hnbaP+X61jArJugnhV05zsI8IJtu4N6whpcHhB4RPKYB4VyFnyr6em6DqY0dXe7bdtm3AgMBAAECgYEAl7/ZbjhAyUooM2lry/N2mATG9COJJKl+Ym/dcWlEjEDaEB0p0WDGDCB3bHUrlCAtSASlw7U9xPRqmo71brDSKU0+PSSjpYPVk2rByL4rqWOhuJM/jz2LkdKhlDJLGu72JpdldA8WVKPpEYpDVUQHiWVVHQFR2u96p45uj2ExjRECQQDLbqmB/NLkJUNHisIzNHDijU+YO6fPN/7zDOlnRjhigY0gTTP4zGUbtFHuTHQsKP2suSNlPFWjlJ+6k4kjpV41AkEAwLMmc4eWsRJematJ03m98mtP6TahoRFvZ47KemAPVTDCjnLTk1n4bQ/4lgydzzSNn9YCUq/YxwWU+/dNZgQFuwJAdF8/lF5+fYhbbmeQJB6RnOfdamZl3oJX083FDxD6XE9j3eCMJH04MZr7a2hM5J4mT1IxT04uZz80CFUxlDSbKQJAfRfPblAm0uxJ3RgE5POzCxv+1DZS1myrFV7ssmSJj5QHuNFx58YQLzye80ldaJWFGq2i9GqTHx/Qh4ETDZau4QJBALFwmdH3I+f5/Fjkc9CNdMOJfdp6JP+vkMjS966IKbhk21PKwAkhmSSMx6CVFuATFovRrWD2YlXfu+4d8HXsTWM=";

            public static final String RSA_PRIVATE = "";
            @Override
            protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
            }
            //Unity中呼叫
            public  void Pay(final String orderInfo)
            {
                if (TextUtils.isEmpty(APPID) || (TextUtils.isEmpty(RSA2_PRIVATE) && TextUtils.isEmpty(RSA_PRIVATE))) {
                    ShowToast("需要配置APPID | RSA_PRIVATE");
                    return;
                }

                //這裡為了測試引數orderInfo並沒有使用,而是直接拼接的orderInfo1
                boolean rsa2 = (RSA2_PRIVATE.length() > 0);
                Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(APPID, rsa2);
                String orderParam = OrderInfoUtil2_0.buildOrderParam(params);

                String privateKey = rsa2 ? RSA2_PRIVATE : RSA_PRIVATE;
                String sign = OrderInfoUtil2_0.getSign(params, privateKey, rsa2);
                final String orderInfo1 = orderParam + "&" + sign;

                Runnable payRunnable=new Runnable() {
                    @Override
                    public void run() {
                        PayTask alipay=new PayTask(MainActivity.this);
                        Map<String,String> result=alipay.payV2(orderInfo1,true);
                        Message msg=new Message();
                        msg.obj=result;
                        mHandler.sendMessage(msg);
                    }
                };

                Thread payThead=new Thread(payRunnable);
                payThead.start();
            }

            public  void ShowToast(final String message)
            {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
                    }
                });
            }
        }

注意:orderInfo可以在客戶端生成,需要AppId,pid以及RSA等等,這樣做不安全,推薦的做法是由服務端生成訂單資訊並加密(生成相關的邏輯在Demo裡已經給出了),然後傳遞給客戶端,客戶端支付完成後,支付寶將執行一個配置好的URL,例如通知服務端支付完畢,而客戶端在支付完成後提示支付成功與否的資訊只能作為參考。

Unity中如何呼叫

直接上程式碼洛:

        using UnityEngine;
        using UnityEngine.UI;
        using System.Collections.Generic;

        [System.Serializable]
        public class PayInfo
        {
            public string subject;  // 顯示在按鈕上的內容,跟支付無關係  
            public float money;     // 商品價錢  
            public string title;    // 商品描述  
        }

        public class AlipayUI : MonoBehaviour
        {
            public List<Button> buttons = null;
            public List<PayInfo> payInfos = null;
            private AndroidJavaObject currentActivity = null;

            void Start()
            {
        #if UNITY_ANDROID && !UNITY_EDITOR  
                // 固定寫法  
                AndroidJavaClass javaClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");  
                currentActivity = javaClass.GetStatic<AndroidJavaObject>("currentActivity");  
        #endif

                // Init UI  
                for (int i = 0; i < buttons.Count; i++)
                {
                    var payInfo = payInfos[i];
                    buttons[i].GetComponentInChildren<Text>().text = payInfos[i].subject;
        #if UNITY_ANDROID && !UNITY_EDITOR  
                    buttons[i].onClick.AddListener(() =>   
                    {  
                         SHow();  
                         AliPay(payInfo);
                    });  
        #endif
                }
            }

            public void SHow()
            {
                currentActivity.Call("ShowToast", "Hello World!");
            }
            public void AliPay(PayInfo payInfo)
            {
                currentActivity.Call("Pay", "ddd");
            }
        }  

上面這個類掛在MainCamera上就行,引數如下圖配置:


選擇Android平臺打包測試即可,額,寫文件好累啊。

附錄

參考連結如下: