Unity Android平臺接入支付寶SDK

這篇文章前面講的創建Android Studio工程的流程是不正確的!!!


最近幫一個羣裏的朋友接了一下支付寶的SDK,開發環境是Unity 5.4.x + 最新的Android Studio2.3版本,最終Android Studio輸出的文件格式是aar而不是之前的jar,鑑於網上的文章大多數是基於Eclipse + jar包,所以記錄一下基於Android Studio進行開發的過程,供大家參考。

下載與整理支付寶SDK

首先下載最新的支付寶SDK,解壓縮後找到alipay_demo和alipay_sdk文件夾,它們分別是Eclipse demo工程和是支付寶的jar包。接下來的步驟的會用到這兩個文件夾裏的內容。

創建並配置Android Studio工程

創建一個新的Android工程,Package Name要和Unity -> PlayerSettings裏的Bundle Identifier保持一致,Minimum API Level最好也保持一致,防止出現意外的問題(沒測過)。
設置Application Name和Company Name。當然Package Name在創建工程後也能修改,不過很麻煩就是了(代碼裏的可以批量修改,但是AndroidManifest裏的需要手動修改)。
設置Minimum API Level,API Level越低所能支持的設備越多,但爲了向下兼容低版本的SDK,高版本的一些特有功能將無法使用。這裏我選擇了API Level 9,即Android 2.3.1,這樣發佈出來的插件可以跑在100%的安卓手機上了。
Unity中Identification設置示例如下(Unity裏的必須是小寫!因爲Android Studio導出的PackageName是小寫):

接着選擇Activity的模版,我們選擇Empty Activity。因爲不需要編寫Android原生界面。

然後是設置Activity的名字界面,因爲我們不需要編程Android界面,所以取消勾選Generate Layout File選項。不取消也可以,可以創建工程後手動刪除Layout文件夾。


至此,完成創建,工程結構應該如圖所示:

這裏面有許多不需要的東西,比如測試工程,圖標資源文件和安卓原生主題的配置文件。我們將其全部刪除,刪除完畢後工程清單如下所示:

可以看到,我們只保留了兩個文件—— AndroidManifest.xml和MainActivity。打開自動生成的AndroidManifest.xml,可以發現有許多關於應用的配置:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.soulgame.magicgame">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>

</manifest>
這裏的android:icon,android:label,android:roundIcon和android:theme是不需要的,刪除它們。同時必須在<activity>標籤裏插入一行<meta-data>屬性,應該是Unity打包時Merge AndroidManifest時需要的,修改後如下所示:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.soulgame.magicgame">

    <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>
最後,因爲我們輸出的是一個庫而不是一個apk,所以需要修改輸出的結果,打開Gradle Scripts(Gradle是一種安卓構建腳本)下的build.gradle(Moudle:app),將第一行的:
apply plugin: 'com.android.application'
修改爲:
apply plugin: 'com.android.library'
點擊上方的sync now,會出現錯誤提示:
Error:Library projects cannot set applicationId. applicationId is set to 'com.soulgame.magicgame' in default config.
這是因爲庫文件不能設置applcation Id,另外build.gradle會包含單元測試的配置,也需要刪除,如下圖所示:

重新點擊sync now,提示錯誤已經消失了。注意這個過程可能會發生其他的問題,如提示build-tools或platform版本過低的提示等等,這時候按照提示給出的方案更新SDK即可。
至此,基本環境配置完畢,接下來就是接入支付寶SDK了。

接入支付寶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.library'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        minSdkVersion 9
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile files('libs/alipaySdk-20170309.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包手動刪除。

接下來就是正式開始參考支付寶提供的Demo和官方文檔寫支付方法了,包括添加混餚規則,修改AndroidManifest.xml增加Activity以及相關的權限設置等等。
其中添加混餚規則需要在Gradle Scripts/proguard-rules.pro裏添加對應的規則:
-libraryjars libs/alipaySDK-20XXXXXX.jar //這裏改成你所使用的SDK 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 *;}

研究支付寶給出的Demo可以發現,支付寶支付函數只需要一個加密後的訂單信息字符串:
public void Pay(final String orderInfo)
{
    Runnable payRunnable = new Runnable()
    {

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

    Thread payThread = new Thread(payRunnable);
    payThread.start();
}
orderInfo可以在客戶端生成,需要AppId,pid以及RSA等等,這樣做不安全,推薦的做法是由服務端生成訂單信息並加密(生成相關的邏輯在Demo裏已經給出了),然後傳遞給客戶端,客戶端支付完成後,支付寶將執行一個配置好的URL,例如通知服務端支付完畢,而客戶端在支付完成後提示支付成功與否的信息只能作爲參考。
最後,附上一份由服務端傳入訂單信息的代碼:
package com.soulgame.magicgame;

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.alipay.sdk.app.PayTask;
import com.unity3d.player.*;

import java.util.Map;

public class MainActivity extends UnityPlayerActivity
{
    private static final int SDK_PAY_FLAG = 1;
    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;
                }
                default:
                    break;
            }
        }

        ;
    };

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
    }
    // Unity中調用
    public void Pay(final String orderInfo)
    {
        Runnable payRunnable = new Runnable()
        {

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

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

在Unity中進行調用

在Assets下創建Plugins文件夾,然後在Plugins下創建Android文件夾,將aar和AndroidManifest.xml文件複製到該文件夾下。使用起來可以參考Unity官方文檔
下面演示了客戶端生成字符串的C#腳本,注意商品描述不能有空格!
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()
    {
        // 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(() => 
            {
                Alipay(payInfo);
            });
#endif
        }
#if UNITY_ANDROID && !UNITY_EDITOR
        // 固定寫法
        AndroidJavaClass javaClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        currentActivity = javaClass.GetStatic<AndroidJavaObject>("currentActivity");
#endif
    }

    public void Alipay(PayInfo payInfo)
    {
        // AlipayClient是Android裏的方法名字,寫死.
        // payInfo.money是要付的錢,只能精確分.
        // payInfo.title是商品描述信息,注意不能有空格.
        currentActivity.Call("AlipayClient", payInfo.money, payInfo.title, "");
    }
}
運行結果:


總結

1.前一部分配置AndroidStudio適用於所有的SDK。
2.支付寶Demo裏有授權信息的方法,手機app內支付用不到,刪除即可。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章