当前位置: 首页 > article >正文

Android应用出海之Klarna登录以及kakao登录

一. Klarna登录

文档入口 klarna开发者官网 👈
入口点进去后选择这个入口点进去 登录SDK接入 👈
在这里插入图片描述
然后就Get start了
在这里插入图片描述
由于是商用,需要注册Klarna开发者帐户,申请API Key 👈是需要申请API Key 才行的,无法注册就需要联系Klarna团队了

下面我直接晒接入操作,代码只是参考,若要真正完成还是需要跟渠道技术团队对接才行。

一. 引入依赖

implementation 'com.klarna.mobile:sdk:2.6.20'

二. 在AndroidMainfest.xml中添加如下代码

        <activity
            android:name="com.klarna.mobile.sdk.activity.KlarnaRedirectReceiverActivity"
            android:exported="true"
            tools:node="replace">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="@string/klarna_login_scheme" />
                <data android:host="devm14.shop.cn" />
                <data android:host="m.shop.com" />
                <data android:host="www.shop.com" />
                <data android:host="de.shop.com" />
                <data android:host="es.shop.com" />
                <data android:host="fr.shop.com" />
                <data android:host="it.shop.com" />
                <data android:host="au.shop.com" />
                <data android:host="stagewww.shop.com" />
                <data android:host="de-m.shop.com" />
                <data android:host="es-m.shop.com" />
                <data android:host="fr-m.shop.com" />
                <data android:host="it-m.shop.com" />
                <data android:host="au-m.shop.com" />
                <data android:host="stagem.shop.com" />
            </intent-filter>
        </activity>

注意:host就是你们的应用支持的域名。

<data android:scheme="@string/klarna_login_scheme" />
<string name="klarna_login_scheme" >klarna1111app</string>

klarna_login_scheme 是特定的,需要申请完API Key后才知道

三. AndroidMainfest.xml文件加入

Provider(与Activity同级)

        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.klarna.fileprovider.share"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"
                tools:replace="android:resource" />
            <!-- <meta-data -->
            <!-- android:name="android.support.FILE_PROVIDER_PATHS" -->
            <!-- android:resource="@xml/klarna_mobile_sdk_share_file_paths" /> -->
        </provider> <!-- facebook provider -->

在res/xml/file_paths中添加下面内容

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <root-path
        name="root"
        path="" />
    <files-path
        name="files"
        path="." />

    <cache-path
        name="cache"
        path="." />

    <external-path
        name="external"
        path="." />

    <external-files-path
        name="external_file_path"
        path="." />
    <external-cache-path
        name="external_cache_path"
        path="." />

    <cache-path path="klarna_mobile_sdk_shared_files/" name="klarna_mobile_sdk_shared_files" />

</paths>

对klarna起作用的是👇这个,其他内容可加可不加,按FileProvider的使用操作 按需使用

<cache-path path="klarna_mobile_sdk_shared_files/" name="klarna_mobile_sdk_shared_files" />

登录核心代码

package com.xxx.manager

import android.app.Activity
import android.text.TextUtils
import com.xxx.BuildConfig
import com.xxx.base.XXXConstant
import com.xxx.ui.event.KlarnaTokenEvent
import com.xxx.ui.mmkv.MMKVSettingHelper
import com.klarna.mobile.sdk.KlarnaMobileSDKError
import com.klarna.mobile.sdk.api.KlarnaEnvironment
import com.klarna.mobile.sdk.api.KlarnaEventHandler
import com.klarna.mobile.sdk.api.KlarnaProductEvent
import com.klarna.mobile.sdk.api.KlarnaRegion
import com.klarna.mobile.sdk.api.component.KlarnaComponent
import com.klarna.mobile.sdk.api.signin.KlarnaSignInError
import com.klarna.mobile.sdk.api.signin.KlarnaSignInEvent
import com.klarna.mobile.sdk.api.signin.KlarnaSignInSDK
import com.klarna.mobile.sdk.api.signin.model.KlarnaSignInToken
import org.greenrobot.eventbus.EventBus

class KlarnaLoginManager private constructor() {

    private var sdkInstance: KlarnaSignInSDK? = null

    companion object {
        val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
            KlarnaLoginManager()
        }
    }

    fun getInstance(): KlarnaSignInSDK? {
        return sdkInstance
    }

    fun initAndLogin(activity: Activity, scope: String, clientId: String, region: String) {
        var emuValue: KlarnaRegion = KlarnaRegion.EU
        if (TextUtils.equals("na", region.lowercase())) {
            emuValue = KlarnaRegion.NA
        }
        var returnUrl = "klarna111xxx://" + XXXConstant.XXX_HOST
        var environment = KlarnaEnvironment.PRODUCTION
        if (BuildConfig.DEBUG) {
            returnUrl = "klarna1111xxx://www.aishop.cn"
            environment = KlarnaEnvironment.PLAYGROUND
        }
        sdkInstance = KlarnaSignInSDK(activity, returnUrl, eventHandler, environment, emuValue)
        sdkInstance?.signIn(
            clientId,
            scope,
            MMKVSettingHelper.getInstance().countryCode,
            MMKVSettingHelper.getInstance().languageCode
        )
    }

    private val eventHandler = object : KlarnaEventHandler {
        override fun onEvent(klarnaComponent: KlarnaComponent, event: KlarnaProductEvent) {
            when (event.action) {
                KlarnaSignInEvent.USER_TAPPED_BUTTON -> {
                    // User tapped the KlarnaSignInButton, auth process starting
                }

                KlarnaSignInEvent.USER_AUTH -> {
                    // User completed interactive auth, tokens will be fetched
                }

                KlarnaSignInEvent.USER_CANCELLED -> {
                    // User manually canceled sign in
                }

                KlarnaSignInEvent.SIGN_IN_TOKEN -> {
                    // User is authorized. You can read the results
                    // in event.params attribute by casting it to the
                    // KlarnaSignInToken model.
                    val token =
                        event.params[KlarnaSignInEvent.ParamKey.KlarnaSignInToken] as KlarnaSignInToken
                    EventBus.getDefault().post(
                        KlarnaTokenEvent(
                            token.accessToken,
                            token.expiresIn,
                            token.idToken,
                            token.refreshToken,
                            token.scope,
                            token.tokenType
                        )
                    )
                }
            }
        }

        override fun onError(klarnaComponent: KlarnaComponent, error: KlarnaMobileSDKError) {
            // In case of any errors, check the 'error' parameter for more details,
            // for example if the error is fatal or not.
            val errorMessage = error.message
            val isFatal = error.isFatal
            when (error.name) {
                KlarnaSignInError.InvalidClientID -> {
                    // The client ID value is invalid
                }

                KlarnaSignInError.InvalidScope -> {
                    // The scope value is invalid
                    // ...
                }

                KlarnaSignInError.InvalidMarket -> {
                    // The market value is invalid
                    // ...
                }

//                KlarnaSignInError.InvalidCustomTabsReturnUrl -> {
//                    // The AndroidManifest needs to be set up for KlarnaCustomTabActivity
//                    // ...
//                }

                KlarnaSignInError.SignInFailed -> {
                    // User authorization step failed
                    val signInError = error.params[KlarnaSignInError.ParamKey.Error]
                    val signInErrorDescription =
                        error.params[KlarnaSignInError.ParamKey.ErrorDescription]
                    // ...
                }

                KlarnaSignInError.RenderButtonFailed -> {
                    // Button failed to render
                    // ...
                }

                KlarnaSignInError.AlreadyInProgress -> {
                    // Sign in is already in progress, user tap or signIn method call is ignored
                    // ...
                }
            }
        }
    }


}

调用流程:

    //klarna登陆
    private fun clickKlarnaLoginAction() {
        val loginMethodList = viewModel.getLoginMethodList()
        val bean = viewModel.getCurrLoginMethodBean()
        if (loginMethodList.isNotEmpty() && loginMethodList.contains(XXXConstant.KLARNA) && bean?.methodExtend != null
        ) {
            //初始化klarna
            klarnaBean = bean.methodExtend?.KLARNA
            KlarnaLoginManager.instance.initAndLogin(
                mActivity,
                bean.methodExtend?.KLARNA?.scope.value(),
                bean.methodExtend?.KLARNA?.clientId.value(),
                bean.methodExtend?.KLARNA?.account.value()
            )
        }
    }

注意:服务端给我们的👇这三个内容很重要,是Klarna登录SDK需要的参数,参考KlarnaLoginManager中的逻辑,

bean.methodExtend?.KLARNA?.scope.value(),//特殊值,可以理解成使用范围,需要在klarna开发者中心配置【不然就是跟klarna技术团队对接的】
bean.methodExtend?.KLARNA?.clientId.value(), //特殊值,需要在klarna开发者中心配置【不然就是跟klarna技术团队对接的】
bean.methodExtend?.KLARNA?.account.value()// 地区[对应region]

如KlarnaLoginManager中的逻辑,成功后会发送EventBus消息,接下来就是拿到Klarna的以下内容。

KlarnaTokenEvent(
token.accessToken,
token.expiresIn,
token.idToken,
token.refreshToken,
token.scope,
token.tokenType
)

👆代码是把accessToken,expiresIn,idToken,refreshToken,scope,tokenType 分装在我们自己的自定义类中。

/**
 * 拿到Klarna的token对象json串的事件
 */
data class KlarnaTokenEvent(
    var accessToken: String?,
    var expiresIn: String?,
    var idToken: String?,
    var refreshToken: String?,
    var scope: String?,
    var tokenType: String?
)

接收到klarna登录成功后,将这些内容分装,再跟服务端约定一种封装算法,将封装好的内容通过登录接口发送请求给服务端进行操作,完成登录即可。

    @Subscribe(threadMode = ThreadMode.MAIN)
    fun loginKlarnaWithToken(event: KlarnaTokenEvent) {
        if (HttpConfig.getInstance().isLogin.not()) {
            val bean = KlarnaParamsBean(
                event.accessToken,
                event.expiresIn,
                event.idToken,
                event.refreshToken,
                event.scope,
                event.tokenType
            )
            viewModel.loginWithThirdMethod(
                Base64.encode(Gson().toJson(bean).toByteArray()),
                "klaran"
            )
        }
    }

结束。详细可以仔细参考Klarna登录官方代码接入演示

二. kakao登录

Android Kakao 登录官方接入指南 👈是官方接入指南。

一. 引入依赖

implementation "com.kakao.sdk:v2-user:2.17.0"

二. 在AndroidMainfest.xml中添加以下代码:

<activity android:name="com.kakao.sdk.auth.AuthCodeHandlerActivity"
          android:exported="true">
          
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        
        <!-- Redirect URI: "kakao${YOUR_NATIVE_APP_KEY}://oauth" -->
        <data android:scheme="kakao${YOUR_NATIVE_APP_KEY}" android:host="oauth" />
    </intent-filter>
</activity>

${YOUR_NATIVE_APP_KEY} 是需要去Kakao开发者去配置应用后获取到的。开发者中心 👈 从这进去后点击MyApplication后登录然后去配置。 参考 👉Kakao开发者APP配置参考文档
在这里插入图片描述
在这里插入图片描述
这个key可以被重新修改的:
如果其中一个应用密钥被泄露,所有者可以重新颁发应用密钥。确保补发是不可逆的。补发后,服务app或网站中的app key必须修改为补发后的值。
👇是APP配置说明
在这里插入图片描述
在这里插入图片描述

三. 使用API

👇这个是我写的管理类, 可直接拷贝,xxx替换下你们的包名即可

package com.xxx.manager;
import android.app.Application;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.xxx.core.XXXApplication;
import com.kakao.sdk.auth.AuthApiClient;
import com.kakao.sdk.auth.model.OAuthToken;
import com.kakao.sdk.common.KakaoSdk;
import com.kakao.sdk.common.model.AuthError;
import com.kakao.sdk.common.model.AuthErrorCause;
import com.kakao.sdk.common.model.ClientError;
import com.kakao.sdk.common.model.ClientErrorCause;
import com.kakao.sdk.common.model.KakaoSdkError;
import com.kakao.sdk.user.UserApiClient;
import com.kakao.sdk.user.model.AccessTokenInfo;
import kotlin.Unit;
import kotlin.jvm.functions.Function2;
// https://developers.kakao.com/docs/latest/en/kakaologin/android#add-modules
public class KakaoLogInManager {
    private static final String TAG = "KakaoLogInManager";
    private static boolean hasInitialized = false;
    /**
     * Kakao Native app key
     */
    public static final String KA_KAO_NATIVE_APP_KEY = "xxxxxx";
    /**
     * Kakao Log In callback
     */
    public interface KakaoLoginCallBack {
        void onGetAccessToken(String accessToken);
    }
    /**
     * Kakao init SDK
     * loginIn和 retryLoginIn和 LogOut会检查是否已初始化,未初始化则会先初始化
     * 默认这里不会产生并发问题
     */
    public static void init(Application application) {
        if (hasInitialized) return;
        KakaoSdk.init(application, KA_KAO_NATIVE_APP_KEY);
        hasInitialized = true;
    }
    /**
     * Kakao Log In
     */
    public static void login(Context context, KakaoLoginCallBack callback) {
        Log.d(TAG, "loginWithKakao start");
        init(XXXApplication.getInstance());
        if (AuthApiClient.getInstance().hasToken()) {
            // Check token presence
            UserApiClient.getInstance().accessTokenInfo(new Function2<AccessTokenInfo, Throwable, Unit>() {
                @Override
                public Unit invoke(AccessTokenInfo accessTokenInfo, Throwable throwable) {
                    if (throwable != null) {
                        retryLogIn(context, callback);
                        showLogInError(throwable);
                    } else {
                        // Succeeded in validating token (Token is refreshed if needed)
                        OAuthToken oAuthToken = AuthApiClient.getInstance().getTokenManagerProvider().getManager().getToken();
                        Log.d(TAG, "Succeeded in validating token " + accessTokenInfo.toString());
                        handleLogInResult(oAuthToken, null, callback);
                        return null;
                    }
                    return null;
                }
            });
        }
        else {
            retryLogIn(context, callback);
        }
    }
    /**
     * Kakao LogIn
     */
    public static void retryLogIn(Context context, KakaoLoginCallBack callback) {
        Log.d(TAG, "loginWithKakao start");
        init(XXXApplication.getInstance());
        if (isKakaoTalkLoginAvailable(context)) {
            UserApiClient.getInstance().loginWithKakaoTalk(context, new Function2<OAuthToken, Throwable, Unit>() {
                @Override
                public Unit invoke(OAuthToken oAuthToken, Throwable throwable) {
                    return handleLogInResult(oAuthToken, throwable, callback);
                }
            });
        } else {
            UserApiClient.getInstance().loginWithKakaoAccount(context, new Function2<OAuthToken, Throwable, Unit>() {
                @Override
                public Unit invoke(OAuthToken oAuthToken, Throwable throwable) {
                    return handleLogInResult(oAuthToken, throwable, callback);
                }
            });
        }
    }
    /**
     * Kakao LogOut
     */
    public static void kakaoLogOut() {
        init(XXXApplication.getInstance());
        if (AuthApiClient.getInstance().hasToken()) {
            UserApiClient.getInstance().logout(throwable -> {
                if (throwable != null) {
                    Log.d(TAG, "Kakao LogOut Fail " + throwable.getMessage());
                } else {
                    Log.d(TAG, "Kakao LogOut Success");
                }
                return null;
            });
        }
    }
    /**
     * check if Kakao Talk has been installed on a user's device
     */
    public static boolean isKakaoTalkLoginAvailable(Context context) {
        boolean isKakaoAvailable = UserApiClient.getInstance().isKakaoTalkLoginAvailable(context);
        Log.d(TAG, "Kakao Talk has been installed:" + isKakaoAvailable);
        return isKakaoAvailable;
    }
    /**
     * handle Log In Result
     */
    private static Unit handleLogInResult(OAuthToken oAuthToken, Throwable throwable, KakaoLoginCallBack callback) {
        Log.d(TAG, "loginWithKakao end");
        if (throwable != null) {
            showLogInError(throwable);
        } else if (oAuthToken != null) {
            String accessToken = oAuthToken.getAccessToken();
            if (!TextUtils.isEmpty(accessToken)) {
                Log.d(TAG, "loginWithKakao accessToken:" + accessToken);
                callback.onGetAccessToken(accessToken);
                return null;
            }
        }
        callback.onGetAccessToken(null);
        return null;
    };
    /**
     * show Log In error info
     */
    private static void showLogInError(Throwable throwable) {
        if (throwable == null) {
            return;
        }
        if (throwable instanceof KakaoSdkError && ((KakaoSdkError) throwable).isInvalidTokenError()) {
            Log.d(TAG, "invalid token error");
        } else if (throwable instanceof ClientError) {
            ClientError clientError = (ClientError) throwable;
            if (clientError != null) {
                ClientErrorCause clientErrorCause = clientError.getReason();
                if (clientErrorCause == ClientErrorCause.Cancelled) {
                    Log.d(TAG, "cancel(back button)", clientError);
                } else if (clientErrorCause == ClientErrorCause.NotSupported) {
                    Log.e(TAG, "not supported(item is not install)", clientError);
                } else {
                    Log.e(TAG, "other client error", clientError);
                }
            }
        } else if (throwable instanceof AuthError) {
            AuthError authError = (AuthError) throwable;
            if (authError != null) {
                AuthErrorCause authErrorCause = authError.getReason();
                if (authErrorCause == AuthErrorCause.AccessDenied) {
                    Log.d(TAG, "cancel(cancel confirm)", authError);
                } else if (authErrorCause == AuthErrorCause.Misconfigured) {
                    Log.e(TAG, "please set android key hash", authError);
                } else {
                    Log.e(TAG, "other auth error", authError);
                }
            }
        } else {
            Log.e(TAG, "other error(net error..)", throwable);
        }
    }
}

调用处非常简单,就是调用👆的KakaoLogInManager.login方法,onGetAccessToken中获取到的it: String? 就是我们的token,只要不为空就说明成功。 代码如下:

    //kakao登录
    private fun clickKakaoLoginAction() {
        //KAKAO登陆
        KakaoLogInManager.login(mActivity, object : KakaoLoginCallBack {
            override fun onGetAccessToken(it: String?) {
                if (!TextUtils.isEmpty(it)) {
                    viewModel.loginWithThirdMethod(
                        it.toString()
                    )
                }
            }
        })
    } 

http://www.kler.cn/a/585851.html

相关文章:

  • 【DeepSeek】一文详解GRPO算法——为什么能减少大模型训练资源?
  • axis=0 和 axis=1的区分设置matplotlib正常显示中文和负号
  • 在 Django 中通过 `/media/xxxx` URL 访问上传资源的安全性与实践
  • 神经网络中常用语言特性(python)(待完善)
  • GD32F4xx系列单片机-串口配合DMA的使用
  • 悬镜夫子ASPM数字供应链安全态势感知平台
  • JAVA中的多态性以及它在实际编程中的作用
  • 前端笔试高频算法题及JavaScript实现
  • iWebOffice2015 中间件如何在Chrome107及之后的高版本中加载
  • wepy微信小程序自定义底部弹出框功能,显示与隐藏效果(淡入淡出,滑入滑出)
  • Linux中grep、sed和awk常见用法总结
  • vscode怎么debug vue项目
  • 68.Harmonyos NEXT 图片预览组件应用实践(一):相册与社交场景
  • C++刷题(一):顺序表 + 单链表
  • OpenHarmony-XTS测试
  • UE5.5 Niagara初始化粒子模块
  • 【技海登峰】Kafka漫谈系列(八)Controller:Zookeeper模式与KRaft模式
  • STAR Decomposition 一种针对极端事件的信号分解方法 论文精读加复现
  • AI大模型测试用例生成平台
  • Nginx正向代理HTTPS配置指南(仅供参考)