欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Unity Android平台接入支付宝

程序员文章站 2024-03-24 13:47:16
...

额,最近折腾在unity项目中接入支付宝,记录下流程。
先来个官方文档:https://docs.open.alipay.com/204/106079/

配置

创建应用,配置应用参考上面的给的官网文档。

支付宝SDK

链接 https://doc.open.alipay.com/doc2/detail.htm?treeId=54&articleId=104509&docType=1

下载SDK&Demo。解压之后会有两个文件夹:alipay_demo和alipay_sdk,别急后面会用到。

创建android库工程

这里用AndroidStudio来创建Android工程,不用Eclipse了,配环境好蛋疼。。。

1.

创建一个新的Android工程,Package Name要和Unity -> PlayerSettings里的Bundle Identifier保持一致。Unity Android平台接入支付宝Unity Android平台接入支付宝

2.

Minimum API Level就直接默认得了,大部分通用
Unity Android平台接入支付宝

3.

选择Activity的模版,我们选择Empty Activity。因为不需要编写Android原生界面。
Unity Android平台接入支付宝

4.

然后是设置Activity的名字界面,因为我们不需要编程Android界面,所以取消勾选Generate Layout File选项。不取消也可以,可以创建工程后手动删除Layout文件夹。
Unity Android平台接入支付宝

5.

创建工作完成,下面对照着下图删除没用东西。

Unity Android平台接入支付宝

只保留了两个文件—— 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如所示:
Unity Android平台接入支付宝

点击旁边的减号,删掉目前这三个以来库,然后点击加号 -> 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,如图:

Unity Android平台接入支付宝

将debug修改为release:

Unity Android平台接入支付宝

最后点击菜单栏Build -> Build APK,稍等,如果出现Build Successfully的提示,说明一切正常。如图:

Unity Android平台接入支付宝

点击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工程中,需要如图这些文件:
Unity Android平台接入支付宝

对就是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中如何调用

unity中调用java有固定的方法,官方文档:https://docs.unity3d.com/ScriptReference/AndroidJavaObject.Call.html

直接上代码洛:

        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上就行,参数如下图配置:

Unity Android平台接入支付宝
Unity Android平台接入支付宝

选择Android平台打包测试即可,额,写文档好累啊。

附录

参考链接如下:

http://blog.csdn.net/zhangdi2017/article/details/63254563
http://www.voidcn.com/blog/qq_16131393/article/p-6168930.html