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平臺打包測試即可,額,寫文件好累啊。
附錄
參考連結如下: