여러분들은 휴대폰에 리더기를 갖다대면 문이열리거나 요금정산이 되는 경험을 해보신 적이 있나요?
이 결제 시스템은 앱 설치 시에 NFC를 이용해 실행 되는데, 앱을 실행하지 않고도 결제를 이용할 수 있어야 합니다.
따라서 저희는 HCE라는 호스트 기반 카드 에뮬레이션을 사용 해야합니다.
HCE을 통해 전자 식별자를 이용해 리더기에 결과값을 넘겨줄 수 있도록 할 수 있는 것이죠.
먼저 HCE의 실행 흐름을 먼저 파악해봅시다.
그렇다면 앱은 모든 NFC Reader에 대해 반응 할까요?
아닙니다. 아까도 언급했듯 전자 식별자를 이용한다고 했는데요. 이 식별자는 AID라고 불리는 것으로 식별이 됩니다.
이 AID는 어떻게 추가하고 사용해야 할까요?
HCE를 사용하기 위해서는 Service를 Extends한 HostApduService라는 것을 사용해야 합니다.
이 HostApduService는 등록한 AID신호를 받았을 경우, 앱 실행 없이도 Service를 통해 해당 클래스가 실행됩니다.
HostApduService 코드를 들여다봅시다
public abstract class HostApduService extends Service {
public static final int DEACTIVATION_DESELECTED = 1;
public static final int DEACTIVATION_LINK_LOSS = 0;
public static final String SERVICE_INTERFACE = "android.nfc.cardemulation.action.HOST_APDU_SERVICE";
public static final String SERVICE_META_DATA = "android.nfc.cardemulation.host_apdu_service";
public HostApduService() {
throw new RuntimeException("Stub!");
}
public final IBinder onBind(Intent intent) {
throw new RuntimeException("Stub!");
}
public final void sendResponseApdu(byte[] responseApdu) {
throw new RuntimeException("Stub!");
}
public final void notifyUnhandled() {
throw new RuntimeException("Stub!");
}
public abstract byte[] processCommandApdu(byte[] var1, Bundle var2);
public abstract void onDeactivated(int var1);
}
몇가지의 메서드가 존재하는데, processCommandApdu는 NFC를통해 AID값을 읽었을 때, 해당 AID값이 byte[]타입인 var1로 들어오게 됩니다.
이 값을 통해 여러 AID값을 분기처리 할 수 있게 됩니다.
그리고 반환값이 존재하는데 이 반환값을 NFC에 전달하게 됨으로써, 서비스는 종료됩니다.
즉 HostApduService는 processCommandApdu라는 메서드를 오버라이딩 하여 해당 부분에 어떤 값을 반환할 것인지에 대해 로직을 추가해야 한다는 것이죠.
Service인 만큼 우리는 매니페스트에 Service에 대한 정의를 해주어야 하는데 매니페스트에는 어떻게 적어야 할까요?
<service android:name=".MyHostApduService" android:exported="true"
android:permission="android.permission.BIND_NFC_SERVICE">
<intent-filter>
<action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
</intent-filter>
<meta-data android:name="android.nfc.cardemulation.host_apdu_service"
android:resource="@xml/apduservice"/>
</service>
위의 코드는 HostApduService에 대한 Manifest 작성 예시입니다.
예시 처럼 Service안에 해당 부분처럼 채워주면 되는데, 다만 여기서 meta-data 안에 있는 resource에 대해 서는 xml을 하나 추가해 등록할 AID에 대한 정보를 제공해야 합니다.
<?xml version="1.0" encoding="utf-8"?>
<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/service_name"
android:requireDeviceUnlock="false">
<aid-group android:description="@string/card_title" android:category="other">
<aid-filter android:name="F222222222"/>
</aid-group>
</host-apdu-service>
XML에 대해 < host-apdu-service> 에 대한 태그를 작성해줘야 하는데, 이 안에는 < aid-group>에 대해 정의를 해주어야 합니다.
그리고 < host-apdu-service> 에 대해 requireDeviceUnlock 설정은 기기 화면이 꺼져 있을 때도 NFC신호를 받을 것인지에 대한 부분인데, 필요에 따라 true, false를 통해 정의해줄 수 있습니다.
이로써, 모바일 결제에 대한 구현부는 모두 구현하였습니다. 앱이 설치되면 NFC를 통해 사용해보실 수 있습니다.
응용 버전으로 AID는 보통 정적인 경우가 많을텐데,솔루션이 필요할 경우에 혹은 하이브리드웹뷰인 경우에는 AID를 동적으로도 추가 할 수가 있는데, 방법은 이렇습니다.
List<String> aids = new ArrayList<>();
aids.add("F0010203040506"); // 예시 AID
// 등록할 AID와 서비스를 지정
ComponentName serviceComponent = new ComponentName(this, MyHostApduService.class);
CardEmulation cardEmulation = CardEmulation.getInstance(NfcAdapter.getDefaultAdapter(this));
cardEmulation.registerAidsForService(serviceComponent, "payment", aids); // payment 혹은 other
등록할 aidList를 작성하고, 카드에뮬레이션에 등록하면 카드 결제서비스에 대한 AID를 덮어쓰기 할 수 있습니다. 보통은 정적으로 xml을 불러오는 것이 좋겠지만 동적으로 사용하고 싶을 때 유용할만 합니다.
사실 자료가 많이 없는 마이너한 클래스라 정보가 틀린 부분이 있을 수도 있습니다. 오류인 부분이 있다면 언제든지 피드백 주시면 다시 검토 후 수정하도록 하겠습니다. 즐거운 코딩되세요 :D
👎👎