Appearance
App2App Android 연동 가이드
App2App 방식으로 모바일 신분증과 연동하려는 Android 앱을 위한 단계별 구현 가이드예요.
1. 인터페이스 방식
App2App direct mode
- 서비스 앱이 직접 모바일 신분증 앱으로 M200 메시지를 전달하고 결과를 SP 서버로 직접 전송해요.

App2App indirect mode
- 서비스 앱이 M200 메시지를 모바일 신분증 앱에 전달하면, 신분증 앱이 결과를 다시 서비스 앱으로 반환해요. 이후 서비스 앱이 SP 서버에 VP를 전송해요.

2. Android 개발 가이드
indirect mode
모바일 신분증 앱 설치 여부 확인
AndroidManifest.xml에 아래 요소를 추가해요.
xml
<manifest>
<queries>
<package android:name="kr.go.mobileid" />
<package android:name="com.samsung.android.ssiframework" />
</queries>
</manifest>앱 설치 여부를 확인하는 코드예요.
kotlin
private const val SCHEME_CA = "mobileid://"
private const val SCHEME_FW = "mobileidfw://"
private var scheme = SCHEME_FW
val intent = Intent("verify", Uri.parse("mobileidfw://verify")).apply {
setPackage("com.samsung.android.ssiframework")
}
val component = intent.resolveActivity(context.packageManager)
if (component == null) {
Toast.makeText(this, "지갑24 앱 미설치", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "지갑24 앱 설치되어 있음", Toast.LENGTH_SHORT).show()
scheme = SCHEME_FW
}서비스 앱에서 SP 서버로 M200 메시지 요청
kotlin
private fun getM200() {
val t530 = T530("indirect", "mdl.1")
httpService.getM200(t530).enqueue(object : Callback<DataWrap> {
override fun onResponse(call: Call<DataWrap>, response: Response<DataWrap>) {
val encodedData = response.body()?.data ?: return
val resM200 = String(Base64.decode(encodedData, Base64.URL_SAFE))
val result = Gson().fromJson(resM200, ResM200::class.java)
callMobileIdApp(result.m200Base64)
}
override fun onFailure(call: Call<DataWrap>, t: Throwable) {
Log.e(TAG, "서버에서 프로필을 가져오는데 실패했습니다.\n${t.message}")
}
})
}모바일 신분증 앱 호출
kotlin
private fun callMobileIdApp(m200: M200) {
val m200Json = gson.toJson(m200)
val m200Base64 = Base64.encodeToString(m200Json.toByteArray(), Base64.URL_SAFE)
val returnScheme = URLEncoder.encode("url scheme", "UTF-8")
val strScheme = "$scheme verify?data_type=byte&data=$m200Base64&clientScheme=$returnScheme"
try {
val intent = Intent("verify", Uri.parse(strScheme)).apply {
if (scheme == SCHEME_FW) {
setPackage("com.samsung.android.ssiframework")
}
}
idApp.launch(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(this, "호출 앱 미설치", Toast.LENGTH_SHORT).show()
}
}모바일 신분증 앱에서 신원 제출 결과 처리
kotlin
private var idApp = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
when (it.resultCode) {
404 -> {
binding.textMessage.text = "FW에 연결된 CA가 없음, 모바일신분증앱도 미설치 상태"
}
Activity.RESULT_OK -> {
val success = it.data?.getBooleanExtra("success", false)
if (success == true) {
val dataType = it.data?.getStringExtra("data_type")
val m400Json = if (dataType == "byte") {
String(it.data?.getByteArrayExtra("data") ?: byteArrayOf())
} else {
it.data?.getStringExtra("data") ?: ""
}
sendVp(m400Json)
} else {
val dataType = it.data?.getStringExtra("data_type")
val m900Json = if (dataType == "byte") {
String(it.data?.getByteArrayExtra("data") ?: byteArrayOf())
} else {
it.data?.getStringExtra("data") ?: ""
}
Log.d(TAG, "M900 = ${String(Base64.decode(m900Json, Base64.URL_SAFE))}")
}
}
Activity.RESULT_CANCELED -> {
binding.textMessage.text = "검증이 사용자 취소로 인해 중단되었습니다."
}
}
}SP 서버로 VP 제출 또는 결과 확인
kotlin
private fun sendVp(m400Json: String) {
// TODO: SP 서버에 제출할 로직 구현
}
private fun checkVp(trxcode: String) {
// TODO: SP 서버에서 결과 확인할 로직 구현
}