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

embedded/2025/3/15 11:40:35/

一. Klarna登录

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

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

一. 引入依赖

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

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

        <activityandroid: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同级)

        <providerandroid:name="androidx.core.content.FileProvider"android:authorities="${applicationId}.klarna.fileprovider.share"android:exported="false"android:grantUriPermissions="true"><meta-dataandroid: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-pathname="root"path="" /><files-pathname="files"path="." /><cache-pathname="cache"path="." /><external-pathname="external"path="." /><external-files-pathname="external_file_path"path="." /><external-cache-pathname="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.managerimport 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.EventBusclass KlarnaLoginManager private constructor() {private var sdkInstance: KlarnaSignInSDK? = nullcompanion 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.EUif (TextUtils.equals("na", region.lowercase())) {emuValue = KlarnaRegion.NA}var returnUrl = "klarna111xxx://" + XXXConstant.XXX_HOSTvar environment = KlarnaEnvironment.PRODUCTIONif (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 KlarnaSignInTokenEventBus.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.messageval isFatal = error.isFatalwhen (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 failedval 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) {//初始化klarnaklarnaBean = bean.methodExtend?.KLARNAKlarnaLoginManager.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 presenceUserApiClient.getInstance().accessTokenInfo(new Function2<AccessTokenInfo, Throwable, Unit>() {@Overridepublic 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>() {@Overridepublic Unit invoke(OAuthToken oAuthToken, Throwable throwable) {return handleLogInResult(oAuthToken, throwable, callback);}});} else {UserApiClient.getInstance().loginWithKakaoAccount(context, new Function2<OAuthToken, Throwable, Unit>() {@Overridepublic 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.ppmy.cn/embedded/172748.html

相关文章

态势感知产品通用的一些安全场景设计

互联网网络攻击类 类别 序号 名称 互联网络攻击类 1 识别DDOS攻击 2 web高频攻击 3 外网主机发起特定端口扫描 4 webshell攻击 5 xss攻击检测 6 SQL注入检测 7 网站挂马 1.1 识别DDOS攻击  场景描述 DDoS攻击通过大量合法的请求占用大量网络、系统资源&#xff0c;以达到瘫痪…

【Go】Go MongoDB 快速入门

1. MongoDB 简介 1.1 MongoDB 介绍 由于我们时常需要存储一些大文本数据&#xff08;比如文章内容&#xff09;&#xff0c;存储到一些关系型数据库可能不是最好的选择&#xff0c;这个时候就需要引入一些 NoSQL&#xff08;Not Only SQL&#xff09;&#xff0c;比如 MongoD…

python web开发django库安装与使用

下面我将指导您如何安装 Django 库以及基本的使用方法。Django 是一个高级的 Python Web 框架&#xff0c;它鼓励快速开发和干净、实用的设计。以下是详细的步骤&#xff1a; 1. 安装 Django 首先&#xff0c;确保您的系统上已经安装了 Python 和 pip&#xff08;Python 的包…

C++:类和对象(从底层编译开始)详解[前篇]

目录 一.inline内联的详细介绍 &#xff08;1&#xff09;为什么在调用内联函数时不需要建立栈帧&#xff1a; &#xff08;2&#xff09;为什么inline声明和定义分离到两个文件会产生链接错误&#xff0c;链接是什么&#xff0c;为什么没有函数地址&#xff1a; 二.类&…

【redis】发布订阅

Redis的发布订阅&#xff08;Pub/Sub&#xff09;是一种基于消息多播的通信机制&#xff0c;它允许消息的**发布者&#xff08;Publisher&#xff09;向特定频道发送消息&#xff0c;而订阅者&#xff08;Subscriber&#xff09;**通过订阅频道或模式来接收消息。 其核心特点如…

DeepSeek进阶应用(一):结合Mermaid绘图(流程图、时序图、类图、状态图、甘特图、饼图)

&#x1f31f;前言: 在软件开发、项目管理和系统设计等领域&#xff0c;图表是表达复杂信息的有效工具。随着AI助手如DeepSeek的普及&#xff0c;我们现在可以更轻松地创建各种专业图表。 名人说&#xff1a;博观而约取&#xff0c;厚积而薄发。——苏轼《稼说送张琥》 创作者&…

探索DB-GPT:革新数据库交互的AI原生框架

引言 在AI与数据技术快速发展的今天,如何让自然语言与数据库实现无缝交互成为开发者关注的重点。DB-GPT作为一个开源的AI原生数据应用开发框架,通过整合大语言模型(LLM)、多代理协作、检索增强生成(RAG)等前沿技术,为开发者提供了高效构建数据驱动型AI应用的解决方案。…

PowerMock的使用

1. mock私有方法 待测试类 public class Demo {public void publicMethod() {System.out.println("public method invoke");protectedMethod("str");privateMethodA();privateMethodB();System.out.println("public method end");}protected v…