├── github.properties ├── app ├── .gitignore ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── subsidian │ │ │ │ └── emvcardsdkdemo │ │ │ │ ├── utils │ │ │ │ ├── Constants.kt │ │ │ │ └── transaction │ │ │ │ │ ├── TMKRequest.kt │ │ │ │ │ ├── TPKRequest.kt │ │ │ │ │ ├── TSKRequest.kt │ │ │ │ │ ├── AIDRequest.kt │ │ │ │ │ ├── CAPKRequest.kt │ │ │ │ │ ├── CallHomeRequest.kt │ │ │ │ │ ├── IPEKEMVRequest.kt │ │ │ │ │ ├── DailyReportRequest.kt │ │ │ │ │ ├── IPEKTrackTwoRequest.kt │ │ │ │ │ ├── TerminalParameterRequest.kt │ │ │ │ │ ├── TransactionUtilities.kt │ │ │ │ │ ├── PurchaseRequest.kt │ │ │ │ │ └── ReversalRequest.kt │ │ │ │ ├── ui │ │ │ │ └── custom │ │ │ │ │ └── KeyboardView.kt │ │ │ │ └── MainActivity.kt │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ ├── drawable │ │ │ │ ├── keyboard_divider.xml │ │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ ├── values │ │ │ │ ├── attr.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ ├── themes.xml │ │ │ │ └── dimens.xml │ │ │ ├── values-night │ │ │ │ └── themes.xml │ │ │ ├── layout │ │ │ │ ├── activity_main.xml │ │ │ │ └── keyboard.xml │ │ │ └── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── subsidian │ │ │ └── emvcardsdkdemo │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── subsidian │ │ └── emvcardsdkdemo │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── emvcardmanager ├── .gitignore ├── consumer-rules.pro ├── libs │ └── j8583-1.13.1.jar ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── subsidian │ │ │ │ └── emvcardmanager │ │ │ │ ├── enums │ │ │ │ ├── ISOAccountType.kt │ │ │ │ ├── ISOCardType.kt │ │ │ │ ├── ISOMessageType.kt │ │ │ │ ├── ISOTransactionTypeName.kt │ │ │ │ ├── ISOTransactionType.kt │ │ │ │ └── ISOProcCode.kt │ │ │ │ ├── interfaces │ │ │ │ ├── SSLProtocol.kt │ │ │ │ ├── SSLKeyManagers.kt │ │ │ │ ├── SSLTrustManagers.kt │ │ │ │ ├── ISOClientEventListener.kt │ │ │ │ ├── ISOClient.kt │ │ │ │ └── SocketHandler.kt │ │ │ │ ├── exceptions │ │ │ │ ├── ISOClientException.kt │ │ │ │ ├── ISOException.kt │ │ │ │ └── KeyException.kt │ │ │ │ ├── entities │ │ │ │ ├── ISOMessage.kt │ │ │ │ ├── ISOMessageContainer.kt │ │ │ │ └── ISOData.kt │ │ │ │ ├── utils │ │ │ │ ├── PrintUtil.kt │ │ │ │ ├── AssetUtil.kt │ │ │ │ ├── TimeUtil.kt │ │ │ │ └── StringUtil.kt │ │ │ │ ├── security │ │ │ │ └── ValueGenerator.kt │ │ │ │ ├── builders │ │ │ │ ├── transactions │ │ │ │ │ ├── ZMKRequestBuilder.kt │ │ │ │ │ ├── TMKRequestBuilder.kt │ │ │ │ │ ├── TPKRequestBuilder.kt │ │ │ │ │ ├── TSKRequestBuilder.kt │ │ │ │ │ ├── IPEKEMVRequestBuilder.kt │ │ │ │ │ ├── IPEKTrackTwoRequestBuilder.kt │ │ │ │ │ ├── AIDRequestBuilder.kt │ │ │ │ │ ├── CAPKRequestBuilder.kt │ │ │ │ │ ├── DailyReportRequestBuilder.kt │ │ │ │ │ ├── TerminalParameterRequestBuilder.kt │ │ │ │ │ └── CallHomeRequestBuilder.kt │ │ │ │ ├── ISOMessageBuilder.kt │ │ │ │ └── ISOClientBuilder.kt │ │ │ │ └── handlers │ │ │ │ ├── IOSocketHandler.kt │ │ │ │ └── NIOSocketHandler.kt │ │ ├── AndroidManifest.xml │ │ └── assets │ │ │ ├── KEYS │ │ │ ├── amex-terminal-config.json │ │ │ ├── paypass-terminal-config.json │ │ │ └── paywave-drl.json │ │ │ └── Docs │ │ │ └── sample_isodata.txt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── subsidian │ │ │ └── emvcardmanager │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── subsidian │ │ └── emvcardmanager │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── .idea ├── .gitignore ├── compiler.xml └── misc.xml ├── gradle.properties ├── .gitignore ├── gradlew.bat └── gradlew /github.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /emvcardmanager/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /emvcardmanager/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | include ':emvcardmanager' 3 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/Constants.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils 2 | 3 | class Constants { 4 | 5 | } -------------------------------------------------------------------------------- /emvcardmanager/libs/j8583-1.13.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsidian-financial-technology-ltd/emv-card-sdk/HEAD/emvcardmanager/libs/j8583-1.13.1.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsidian-financial-technology-ltd/emv-card-sdk/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsidian-financial-technology-ltd/emv-card-sdk/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsidian-financial-technology-ltd/emv-card-sdk/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsidian-financial-technology-ltd/emv-card-sdk/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsidian-financial-technology-ltd/emv-card-sdk/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsidian-financial-technology-ltd/emv-card-sdk/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsidian-financial-technology-ltd/emv-card-sdk/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsidian-financial-technology-ltd/emv-card-sdk/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsidian-financial-technology-ltd/emv-card-sdk/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/subsidian-financial-technology-ltd/emv-card-sdk/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/enums/ISOAccountType.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.enums 2 | 3 | enum class ISOAccountType(val value: String) { 4 | DEFAULT_ACCTOUNT_TYPE("00"), 5 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/interfaces/SSLProtocol.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.interfaces 2 | 3 | interface SSLProtocol { 4 | fun setSSLProtocol(protocol: String): SSLKeyManagers 5 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/enums/ISOCardType.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.enums 2 | 3 | enum class ISOCardType(val value: String) { 4 | MAG_CARD_TYPE("02"), 5 | ICC_CARD_TYPE("05"), 6 | PICC_CARD_TYPE("07"), 7 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/interfaces/SSLKeyManagers.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.interfaces 2 | 3 | import javax.net.ssl.KeyManager 4 | 5 | interface SSLKeyManagers { 6 | fun setKeyManagers(keyManagers: Array): SSLTrustManagers 7 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/keyboard_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/exceptions/ISOClientException.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.exceptions 2 | 3 | import java.lang.Exception 4 | 5 | class ISOClientException : Exception { 6 | constructor(message: String?) : super(message) {} 7 | constructor(e: Exception?) : super(e) {} 8 | } -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/exceptions/ISOException.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.exceptions 2 | 3 | import java.lang.Exception 4 | 5 | class ISOException : Exception { 6 | constructor(message: String?) : super(message) {} 7 | constructor(message: String?, cause: Throwable?) : super(message, cause) {} 8 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/exceptions/KeyException.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.exceptions 2 | 3 | import java.lang.Exception 4 | 5 | class KeyException : Exception { 6 | constructor(message: String?) : super(message) {} 7 | constructor(message: String?, cause: Throwable?) : super(message, cause) {} 8 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/interfaces/SSLTrustManagers.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.interfaces 2 | 3 | import com.subsidian.emvcardmanager.builders.ISOClientBuilder.ClientBuilder 4 | import javax.net.ssl.TrustManager 5 | 6 | interface SSLTrustManagers { 7 | fun setTrustManagers(trustManagers: Array): ClientBuilder 8 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/enums/ISOMessageType.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.enums 2 | 3 | enum class ISOMessageType(val value: String) { 4 | _0800("0800"), 5 | _0810("0810"), 6 | _0100("0100"), 7 | _0200("0200"), 8 | _0210("0210"), 9 | _0220("0220"), 10 | _0420("0420"), 11 | _0421("0421"), 12 | _0500("0500"), 13 | _0320("0320"), 14 | } -------------------------------------------------------------------------------- /app/src/test/java/com/subsidian/emvcardsdkdemo/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /emvcardmanager/src/test/java/com/subsidian/emvcardmanager/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values/attr.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/interfaces/ISOClientEventListener.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.interfaces 2 | 3 | interface ISOClientEventListener { 4 | fun connecting() 5 | fun connected() 6 | fun connectionFailed() 7 | fun connectionClosed() 8 | fun connectionTimeout() 9 | fun disconnected() 10 | fun beforeSendingMessage() 11 | fun afterSendingMessage() 12 | fun onReceiveData() 13 | fun beforeReceiveResponse() 14 | fun afterReceiveResponse() 15 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/assets/KEYS/amex-terminal-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "kernelId": "04", 3 | "terminalType": "21", 4 | "terminalFloorLimit": "000000020000", 5 | "terminalCountryCode": "0620", 6 | "additionalTerminalCapability": "7000F0A001", 7 | "terminalCapability": "E078C8", 8 | "enhanceContactlessReaderCapability": "D8B04000", 9 | "propKernelConfig": "093C1500", 10 | "kernel4ReaderCapability": "C0", 11 | "defaultDRLFloorLimit": "000000000000", 12 | "defaultDRLCvmLimit": "000000000200", 13 | "defaultDRLTransactionLimit": "000000000700" 14 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/enums/ISOTransactionTypeName.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.enums 2 | 3 | enum class ISOTransactionTypeName(val value: String) { 4 | PURCHASE("Purchase"), 5 | BALANCE("Balance"), 6 | REFUND("Refund"), 7 | CASH_ADVANCE("Cash Advance"), 8 | CASH_BACK("Cash Back"), 9 | PRE_AUTH("Pre-Authorization"), 10 | PRE_AUTH_COMPLETION("Pre-Authorization Completion"), 11 | CARD_VERIFICATION("Card Verification"), 12 | TRANSACTION("Transaction"), 13 | SETTLEMENT("Settlement"), 14 | } -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #575757 10 | #FFFFFF 11 | #613AEA 12 | #0033B3 13 | #E2E3E6 14 | #F44336 15 | -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/enums/ISOTransactionType.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.enums 2 | 3 | enum class ISOTransactionType(val value: String) { 4 | PURCHASE_TRANSACTION_TYPE("00"), 5 | BALANCE_TRANSACTION_TYPE("31"), 6 | REFUND_TRANSACTION_TYPE("20"), 7 | CASH_BACK_TRANSACTION_TYPE("09"), 8 | CASH_ADVANCE_TRANSACTION_TYPE("01"), 9 | PRE_AUTH_TRANSACTION_TYPE("60"), 10 | PRE_AUTH_COMPLETION_TRANSACTION_TYPE("61"), 11 | CASH_TRANSACTION_TRANSACTION_TYPE("42"), 12 | SETTLEMENT_TRANSACTION_TYPE("92"), 13 | CARD_VERIFICATION_TRANSACTION_TYPE("15"), 14 | SETTLEMENT_BATCH_UPLOAD_CLOSE_TRANSACTION_TYPE("96"), 15 | } -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/enums/ISOProcCode.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.enums 2 | 3 | enum class ISOProcCode(val value: String) { 4 | TMK_DOWNLOAD_ISO_PROC_CODE("9A0000"), 5 | TSK_DOWNLOAD_ISO_PROC_CODE("9B0000"), 6 | TERM_PARAM_DOWNLOAD_ISO_PROC_CODE("9C0000"), 7 | CALL_HOME_ISO_PROC_CODE("9D0000"), 8 | CAPK_DOWNLOAD_ISO_PROC_CODE("9E0000"), 9 | AID_DOWNLOAD_ISO_PROC_CODE("9F0000"), 10 | TPK_DOWNLOAD_ISO_PROC_CODE("9G0000"), 11 | DAILY_REPORT_DOWNLOAD_ISO_PROC_CODE("9H0000"), 12 | IPEK_TRACK2_DOWNLOAD_ISO_PROC_CODE("9I0000"), 13 | IPEK_EMV_DOWNLOAD_ISO_PROC_CODE("9J0000"), 14 | SETTLEMENT_ISO_PROC_CODE("920000"), 15 | SETTLEMENT_BATCH_UPLOAD_CLOSE_ISO_PROC_CODE("960000"), 16 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/subsidian/emvcardsdkdemo/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.subsidian.app", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | EMV ISO8583 SDK(demo) 3 | 4 | 0 5 | 1 6 | 2 7 | 3 8 | 4 9 | 5 10 | 6 11 | 7 12 | 8 13 | 9 14 | Clear 15 | Backspace 16 | \u232b 17 | + 18 | Make Payment 19 | 20 | -------------------------------------------------------------------------------- /emvcardmanager/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /emvcardmanager/src/androidTest/java/com/subsidian/emvcardmanager/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.subsidian.emvcardmanager.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/entities/ISOMessage.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.entities 2 | 3 | import com.solab.iso8583.IsoMessage 4 | 5 | class ISOMessage() { 6 | 7 | private var isoMessage: IsoMessage = IsoMessage() 8 | 9 | constructor(isoMessage: IsoMessage) : this() { 10 | this.isoMessage = isoMessage 11 | } 12 | 13 | fun getMessage(): IsoMessage { 14 | return this.isoMessage 15 | } 16 | 17 | fun getMessageType(formatType: MessageTypeFormat = MessageTypeFormat.HEXA): String { 18 | return this.isoMessage.type.toString(formatType.value) 19 | } 20 | 21 | companion object { 22 | enum class MessageTypeFormat(val value: Int){ 23 | HEXA(16), 24 | DECIMAL(10), 25 | OCTAL(8), 26 | BINARY(2) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/TMKRequest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.builders.ISODataBuilder 4 | import com.subsidian.emvcardmanager.entities.ISOData 5 | import com.subsidian.emvcardmanager.enums.ISOProcCode 6 | 7 | object TMKRequest { 8 | 9 | private val transactionUtilities = TransactionUtilities() 10 | 11 | fun build (): ISOData { 12 | return ISODataBuilder.Builder() 13 | .processingCode(ISOProcCode.TMK_DOWNLOAD_ISO_PROC_CODE.value) 14 | .transmissionDateTime(transactionUtilities.transmissionDateTime()) 15 | .systemTraceAuditNumber(transactionUtilities.systemTraceAuditNumber()) 16 | .localTime(transactionUtilities.localTime()) 17 | .localDate(transactionUtilities.localDate()) 18 | .cardAcceptorTerminalId(transactionUtilities.cardAcceptorTerminalId()) 19 | .build() 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/TPKRequest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.builders.ISODataBuilder 4 | import com.subsidian.emvcardmanager.entities.ISOData 5 | import com.subsidian.emvcardmanager.enums.ISOProcCode 6 | 7 | object TPKRequest { 8 | 9 | private val transactionUtilities = TransactionUtilities() 10 | 11 | fun build (): ISOData { 12 | return ISODataBuilder.Builder() 13 | .processingCode(ISOProcCode.TPK_DOWNLOAD_ISO_PROC_CODE.value) 14 | .transmissionDateTime(transactionUtilities.transmissionDateTime()) 15 | .systemTraceAuditNumber(transactionUtilities.systemTraceAuditNumber()) 16 | .localTime(transactionUtilities.localTime()) 17 | .localDate(transactionUtilities.localDate()) 18 | .cardAcceptorTerminalId(transactionUtilities.cardAcceptorTerminalId()) 19 | .build() 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/TSKRequest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.builders.ISODataBuilder 4 | import com.subsidian.emvcardmanager.entities.ISOData 5 | import com.subsidian.emvcardmanager.enums.ISOProcCode 6 | 7 | object TSKRequest { 8 | 9 | private val transactionUtilities = TransactionUtilities() 10 | 11 | fun build (): ISOData { 12 | return ISODataBuilder.Builder() 13 | .processingCode(ISOProcCode.TSK_DOWNLOAD_ISO_PROC_CODE.value) 14 | .transmissionDateTime(transactionUtilities.transmissionDateTime()) 15 | .systemTraceAuditNumber(transactionUtilities.systemTraceAuditNumber()) 16 | .localTime(transactionUtilities.localTime()) 17 | .localDate(transactionUtilities.localDate()) 18 | .cardAcceptorTerminalId(transactionUtilities.cardAcceptorTerminalId()) 19 | .build() 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/AIDRequest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.builders.ISODataBuilder 4 | import com.subsidian.emvcardmanager.entities.ISOData 5 | import com.subsidian.emvcardmanager.enums.ISOProcCode 6 | 7 | object AIDRequest { 8 | 9 | private val transactionUtilities = TransactionUtilities() 10 | 11 | fun build (): ISOData { 12 | return ISODataBuilder.Builder() 13 | .processingCode(ISOProcCode.AID_DOWNLOAD_ISO_PROC_CODE.value) 14 | .transmissionDateTime(transactionUtilities.transmissionDateTime()) 15 | .systemTraceAuditNumber(transactionUtilities.systemTraceAuditNumber()) 16 | .localTime(transactionUtilities.localTime()) 17 | .localDate(transactionUtilities.localDate()) 18 | .cardAcceptorTerminalId(transactionUtilities.cardAcceptorTerminalId()) 19 | .primaryMessageHashValue(transactionUtilities.primaryMessageHashValue()) 20 | .build() 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/CAPKRequest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.builders.ISODataBuilder 4 | import com.subsidian.emvcardmanager.entities.ISOData 5 | import com.subsidian.emvcardmanager.enums.ISOProcCode 6 | 7 | object CAPKRequest { 8 | 9 | private val transactionUtilities = TransactionUtilities() 10 | 11 | fun build (): ISOData { 12 | return ISODataBuilder.Builder() 13 | .processingCode(ISOProcCode.CAPK_DOWNLOAD_ISO_PROC_CODE.value) 14 | .transmissionDateTime(transactionUtilities.transmissionDateTime()) 15 | .systemTraceAuditNumber(transactionUtilities.systemTraceAuditNumber()) 16 | .localTime(transactionUtilities.localTime()) 17 | .localDate(transactionUtilities.localDate()) 18 | .cardAcceptorTerminalId(transactionUtilities.cardAcceptorTerminalId()) 19 | .primaryMessageHashValue(transactionUtilities.primaryMessageHashValue()) 20 | .build() 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/CallHomeRequest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.builders.ISODataBuilder 4 | import com.subsidian.emvcardmanager.entities.ISOData 5 | import com.subsidian.emvcardmanager.enums.ISOProcCode 6 | 7 | object CallHomeRequest { 8 | 9 | private val transactionUtilities = TransactionUtilities() 10 | 11 | fun build (): ISOData { 12 | return ISODataBuilder.Builder() 13 | .processingCode(ISOProcCode.CALL_HOME_ISO_PROC_CODE.value) 14 | .transmissionDateTime(transactionUtilities.transmissionDateTime()) 15 | .systemTraceAuditNumber(transactionUtilities.systemTraceAuditNumber()) 16 | .localTime(transactionUtilities.localTime()) 17 | .localDate(transactionUtilities.localDate()) 18 | .cardAcceptorTerminalId(transactionUtilities.cardAcceptorTerminalId()) 19 | .primaryMessageHashValue(transactionUtilities.primaryMessageHashValue()) 20 | .build() 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/IPEKEMVRequest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.builders.ISODataBuilder 4 | import com.subsidian.emvcardmanager.entities.ISOData 5 | import com.subsidian.emvcardmanager.enums.ISOProcCode 6 | 7 | object IPEKEMVRequest { 8 | 9 | private val transactionUtilities = TransactionUtilities() 10 | 11 | fun build (): ISOData { 12 | return ISODataBuilder.Builder() 13 | .processingCode(ISOProcCode.IPEK_EMV_DOWNLOAD_ISO_PROC_CODE.value) 14 | .transmissionDateTime(transactionUtilities.transmissionDateTime()) 15 | .systemTraceAuditNumber(transactionUtilities.systemTraceAuditNumber()) 16 | .localTime(transactionUtilities.localTime()) 17 | .localDate(transactionUtilities.localDate()) 18 | .cardAcceptorTerminalId(transactionUtilities.cardAcceptorTerminalId()) 19 | .primaryMessageHashValue(transactionUtilities.primaryMessageHashValue()) 20 | .build() 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/assets/KEYS/paypass-terminal-config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "enableMaxLifeTimeTorn": "01", 4 | "maxLifeTimeTorn": "003C", 5 | "enableMaxNumberTorn": "01", 6 | "maxNumberTorn": "01", 7 | "enableBalanceReadBeforeGAC": "", 8 | "balanceReadBeforeGAC": "", 9 | "enableBalanceReadAfterGAC": "", 10 | "balanceReadAfterGAC": "", 11 | "enableMobileSupportIndicator": "01", 12 | "mobileSupportIndicator": "02", 13 | "enableHoldTimeValue": "", 14 | "holdTimeValue": "", 15 | "enableInterfaceDeviceSN": "", 16 | "interfaceDeviceSN": "", 17 | "enableKernelId": "", 18 | "kernelId": "", 19 | "enableMessageHoldTime": "", 20 | "messageHoldTime": "", 21 | "minimumRRGP": "", 22 | "maximumRRGP": "", 23 | "terminalETTFRRCApdu": "", 24 | "terminalETTFRRRApdu": "", 25 | "rrat": "", 26 | "rrttmt": "", 27 | "enableMinimumRRGP": "", 28 | "enableMaximumRRGP": "", 29 | "enableTerminalETTFRRCApdu": "", 30 | "enableTerminalETTFRRRApdu": "", 31 | "enableRRAT": "", 32 | "enableRRTTMT": "" 33 | } 34 | ] -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/DailyReportRequest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.builders.ISODataBuilder 4 | import com.subsidian.emvcardmanager.entities.ISOData 5 | import com.subsidian.emvcardmanager.enums.ISOProcCode 6 | 7 | object DailyReportRequest { 8 | 9 | private val transactionUtilities = TransactionUtilities() 10 | 11 | fun build (): ISOData { 12 | return ISODataBuilder.Builder() 13 | .processingCode(ISOProcCode.DAILY_REPORT_DOWNLOAD_ISO_PROC_CODE.value) 14 | .transmissionDateTime(transactionUtilities.transmissionDateTime()) 15 | .systemTraceAuditNumber(transactionUtilities.systemTraceAuditNumber()) 16 | .localTime(transactionUtilities.localTime()) 17 | .localDate(transactionUtilities.localDate()) 18 | .cardAcceptorTerminalId(transactionUtilities.cardAcceptorTerminalId()) 19 | .primaryMessageHashValue(transactionUtilities.primaryMessageHashValue()) 20 | .build() 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/IPEKTrackTwoRequest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.builders.ISODataBuilder 4 | import com.subsidian.emvcardmanager.entities.ISOData 5 | import com.subsidian.emvcardmanager.enums.ISOProcCode 6 | 7 | object IPEKTrackTwoRequest { 8 | 9 | private val transactionUtilities = TransactionUtilities() 10 | 11 | fun build (): ISOData { 12 | return ISODataBuilder.Builder() 13 | .processingCode(ISOProcCode.IPEK_TRACK2_DOWNLOAD_ISO_PROC_CODE.value) 14 | .transmissionDateTime(transactionUtilities.transmissionDateTime()) 15 | .systemTraceAuditNumber(transactionUtilities.systemTraceAuditNumber()) 16 | .localTime(transactionUtilities.localTime()) 17 | .localDate(transactionUtilities.localDate()) 18 | .cardAcceptorTerminalId(transactionUtilities.cardAcceptorTerminalId()) 19 | .primaryMessageHashValue(transactionUtilities.primaryMessageHashValue()) 20 | .build() 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/TerminalParameterRequest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.builders.ISODataBuilder 4 | import com.subsidian.emvcardmanager.entities.ISOData 5 | import com.subsidian.emvcardmanager.enums.ISOProcCode 6 | 7 | object TerminalParameterRequest { 8 | 9 | private val transactionUtilities = TransactionUtilities() 10 | 11 | fun build (): ISOData { 12 | return ISODataBuilder.Builder() 13 | .processingCode(ISOProcCode.TERM_PARAM_DOWNLOAD_ISO_PROC_CODE.value) 14 | .transmissionDateTime(transactionUtilities.transmissionDateTime()) 15 | .systemTraceAuditNumber(transactionUtilities.systemTraceAuditNumber()) 16 | .localTime(transactionUtilities.localTime()) 17 | .localDate(transactionUtilities.localDate()) 18 | .cardAcceptorTerminalId(transactionUtilities.cardAcceptorTerminalId()) 19 | .primaryMessageHashValue(transactionUtilities.primaryMessageHashValue()) 20 | .build() 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## For more details on how to configure your build environment visit 2 | # http://www.gradle.org/docs/current/userguide/build_environment.html 3 | # 4 | # Specifies the JVM arguments used for the daemon process. 5 | # The setting is particularly useful for tweaking memory settings. 6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 7 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 8 | # 9 | # When configured, Gradle will run in incubating parallel mode. 10 | # This option should only be used with decoupled projects. More details, visit 11 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 12 | # org.gradle.parallel=true 13 | # Fri Oct 30 07:03:11 WAT 2020 14 | ANDROID_KEY_PASSWORD=0987654321 15 | ANDROID_STORE_PASSWORD=0987654321 16 | android.databinding.incremental=true 17 | android.enableJetifier=true 18 | android.enableR8.fullMode=false 19 | android.lifecycleProcessor.incremental=true 20 | android.useAndroidX=true 21 | android.useDeprecatedNdk=true 22 | kapt.include.compile.classpath=false 23 | kapt.incremental.apt=true 24 | kapt.use.worker.api=true 25 | kapt.verbose=true 26 | kotlin.code.style=official 27 | org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" 28 | # Disble testOnly mode for Android Studio 29 | -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/interfaces/ISOClient.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.interfaces 2 | 3 | import com.subsidian.emvcardmanager.builders.ISOMessageBuilder 4 | import com.subsidian.emvcardmanager.exceptions.ISOClientException 5 | import java.io.IOException 6 | 7 | interface ISOClient { 8 | /** 9 | * 10 | * @throws ISOClientException 11 | * @throws IOException 12 | */ 13 | @Throws(ISOClientException::class, IOException::class) 14 | fun connect() 15 | 16 | /** 17 | * 18 | */ 19 | fun disconnect() 20 | 21 | /** 22 | * 23 | * @param packBuilder 24 | * @return 25 | * @throws ISOClientException 26 | * @throws IOException 27 | */ 28 | @Throws(ISOClientException::class, IOException::class) 29 | fun sendMessageSync(packBuilder: ISOMessageBuilder.PackBuilder): ByteArray 30 | 31 | /** 32 | * 33 | * 34 | * @return 35 | */ 36 | @Throws(ISOClientException::class, IOException::class) 37 | fun isConnected(): Boolean 38 | 39 | /** 40 | * 41 | * @return 42 | */ 43 | @Throws(ISOClientException::class, IOException::class) 44 | fun isClosed(): Boolean 45 | 46 | /** 47 | * 48 | * @param isoClientEventListener 49 | */ 50 | fun setEventListener(isoClientEventListener: ISOClientEventListener?) 51 | } -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | 6 | android { 7 | compileSdk 31 8 | 9 | defaultConfig { 10 | applicationId "com.subsidian.sdkdemo" 11 | minSdk 22 12 | targetSdk 31 13 | versionCode 1 14 | versionName "1.0" 15 | 16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | kotlinOptions { 30 | jvmTarget = '1.8' 31 | } 32 | } 33 | 34 | repositories { 35 | mavenCentral() 36 | } 37 | 38 | dependencies { 39 | implementation fileTree(include: ['*.jar'], dir: 'libs') 40 | implementation project(path: ":emvcardmanager") 41 | implementation 'androidx.appcompat:appcompat:1.3.1' 42 | implementation 'androidx.core:core-ktx:1.6.0' 43 | implementation 'com.android.support.constraint:constraint-layout:2.0.4' 44 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3") 45 | testImplementation 'junit:junit:4.+' 46 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 47 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 48 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/utils/PrintUtil.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.utils 2 | 3 | import android.util.Log 4 | import com.subsidian.emvcardmanager.entities.ISOMessage 5 | 6 | object PrintUtil { 7 | 8 | fun dumpIsoMessages(message: ISOMessage, isRequest: Boolean) { 9 | val isoString = StringBuilder() 10 | isoString.append("-\n") 11 | isoString.append("============================================\n") 12 | isoString.append("ISO DATA DUMP(${VariableCheckUtil.isoMessageType(message)} - ${if (isRequest){"Request"} else {"Response"}})\n") 13 | isoString.append("============================================\n") 14 | isoString.append("ISO TYPE: ${message.getMessageType()}\n") 15 | isoString.append("============================================\n") 16 | isoString.append("Debug Message: ${message.getMessage().debugString()}\n") 17 | isoString.append("============================================\n") 18 | try { 19 | for (i in 0..128) { 20 | if (message.getMessage().hasField(i)) { 21 | isoString.append( 22 | "Field $i (${VariableCheckUtil.isoFieldName(i)}) => ${message.getMessage().getField(i)} \n" 23 | ) 24 | } 25 | } 26 | } catch (ex: Exception) { 27 | ex.printStackTrace() 28 | } finally { 29 | isoString.append("============================================\n") 30 | Log.d(this.javaClass.simpleName, isoString.toString()) 31 | } 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | # Uncomment the following line in case you need and you don't have the release build type files in your app 18 | # release/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # IntelliJ 40 | *.iml 41 | idea/ 42 | .idea/ 43 | .idea/workspace.xml 44 | .idea/tasks.xml 45 | .idea/gradle.xml 46 | .idea/assetWizardSettings.xml 47 | .idea/dictionaries 48 | .idea/libraries 49 | # Android Studio 3 in .gitignore file. 50 | .idea/caches 51 | .idea/modules.xml 52 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 53 | .idea/navEditor.xml 54 | 55 | # Keystore files 56 | # Uncomment the following lines if you do not want to check your keystore files in. 57 | #*.jks 58 | #*.keystore 59 | 60 | # External native build folder generated in Android Studio 2.2 and later 61 | .externalNativeBuild 62 | .cxx/ 63 | 64 | # Google Services (e.g. APIs or Firebase) 65 | # google-services.json 66 | 67 | # Freeline 68 | freeline.py 69 | freeline/ 70 | freeline_project_description.json 71 | 72 | # fastlane 73 | fastlane/report.xml 74 | fastlane/Preview.html 75 | fastlane/screenshots 76 | fastlane/test_output 77 | fastlane/readme.md 78 | 79 | # Version control 80 | vcs.xml 81 | 82 | # lint 83 | lint/intermediates/ 84 | lint/generated/ 85 | lint/outputs/ 86 | lint/tmp/ 87 | # lint/reports/ 88 | gradle/wrapper/gradle-wrapper.jar 89 | gradle/ 90 | 91 | github.properties 92 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 22 | 23 | 28 | 29 | 41 | 42 | 46 | -------------------------------------------------------------------------------- /emvcardmanager/src/main/assets/Docs/sample_isodata.txt: -------------------------------------------------------------------------------- 1 | ISODataBuilder.Builder() 2 | .primaryAccountNumber("") 3 | .processingCode("") 4 | .transactionAmount("") 5 | .transmissionDateTime("") 6 | .settlementConversionRate("") 7 | .systemTraceAuditNumber("") 8 | .localTime("") 9 | .localDate("") 10 | .expirationDate("") 11 | .settlementDate("") 12 | .conversionDate("") 13 | .merchantType("") 14 | .posEntryMode("") 15 | .cardSequenceNumber("") 16 | .posConditionCode("") 17 | .posPinCaptureCode("") 18 | .transactionFeeAmount("") 19 | .settlementAmount("") 20 | .transactionProcessingFeeAmount("") 21 | .settleProcessingFeeAmount("") 22 | .acquiringInstitutionId("") 23 | .forwardingInstitutionId("") 24 | .trackTwoData("") 25 | .retrievalReferenceNumber("") 26 | .authorizationIdResponse("") 27 | .responseCode("") 28 | .serviceRestrictionCode("") 29 | .cardAcceptorTerminalId("") 30 | .cardAcceptorIdCode("") 31 | .cardAcceptorNameLocation("") 32 | .additionalResponseData("") 33 | .additionalData("") 34 | .transactionCurrencyCode("") 35 | .settlementCurrencyCode("") 36 | .pinData("") 37 | .securityRelatedControlInformation("") 38 | .additionalAmounts("") 39 | .integratedCircuitCardData("") 40 | .messageResponseCode("") 41 | .authorizingAgentId("") 42 | .transportEchoData("") 43 | .paymentInformation("") 44 | .managementDataOnePrivate("") 45 | .managementDataTwoPrivate("") 46 | .primaryMessageHashValue("") 47 | .extendedPaymentCode("") 48 | .originalDataElement("") 49 | .replacementAmount("") 50 | .payee("") 51 | .receivingInstitutionId("") 52 | .accountIdentification1("") 53 | .accountIdentification2("") 54 | .posDataCode("") 55 | .nearFieldCommunicationData("") 56 | .secondaryMessageHashValue("") 57 | .build() 58 | -------------------------------------------------------------------------------- /emvcardmanager/src/main/assets/KEYS/paywave-drl.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "programId": "0100000000000000000000000000000000", 4 | "programIdLen": "01", 5 | "drlEnableFlag": "01", 6 | "enableStatusCheck": "00", 7 | "enableAmountZeroOption": "00", 8 | "amountZeroOption": "01", 9 | "enableTransLimit": "01", 10 | "enableCVMLimit": "01", 11 | "enableFloorLimit": "01", 12 | "contactlessTransLimit": "D007000000000000", 13 | "contactlessCvmLimit": "F401000000000000", 14 | "contactlessFloorLimit": "E803000000000000", 15 | "terminalTranQualifiers": "00", 16 | "contactlessNotAllowFlag": "00" 17 | }, 18 | { 19 | "programId": "0200000000000000000000000000000000", 20 | "programIdLen": "01", 21 | "drlEnableFlag": "01", 22 | "enableStatusCheck": "00", 23 | "enableAmountZeroOption": "01", 24 | "amountZeroOption": "00", 25 | "enableTransLimit": "00", 26 | "enableCVMLimit": "01", 27 | "enableFloorLimit": "01", 28 | "contactlessTransLimit": "D007000000000000", 29 | "contactlessCvmLimit": "F401000000000000", 30 | "contactlessFloorLimit": "E803000000000000", 31 | "terminalTranQualifiers": "00", 32 | "contactlessNotAllowFlag": "00" 33 | }, 34 | { 35 | "programId": "0300000000000000000000000000000000", 36 | "programIdLen": "01", 37 | "drlEnableFlag": "01", 38 | "enableStatusCheck": "00", 39 | "enableAmountZeroOption": "01", 40 | "amountZeroOption": "00", 41 | "enableTransLimit": "01", 42 | "enableCVMLimit": "01", 43 | "enableFloorLimit": "01", 44 | "contactlessTransLimit": "1027000000000000", 45 | "contactlessCvmLimit": "E803000000000000", 46 | "contactlessFloorLimit": "E803000000000000", 47 | "terminalTranQualifiers": "00", 48 | "contactlessNotAllowFlag": "00" 49 | }, 50 | { 51 | "programId": "0400000000000000000000000000000000", 52 | "programIdLen": "01", 53 | "drlEnableFlag": "01", 54 | "enableStatusCheck": "00", 55 | "enableAmountZeroOption": "01", 56 | "amountZeroOption": "00", 57 | "enableTransLimit": "01", 58 | "enableCVMLimit": "01", 59 | "enableFloorLimit": "01", 60 | "contactlessTransLimit": "1027000000000000", 61 | "contactlessCvmLimit": "E803000000000000", 62 | "contactlessFloorLimit": "8813000000000000", 63 | "terminalTranQualifiers": "00", 64 | "contactlessNotAllowFlag": "00" 65 | } 66 | ] -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/security/ValueGenerator.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.security 2 | 3 | import com.subsidian.emvcardmanager.utils.StringUtil 4 | import java.nio.ByteBuffer 5 | import java.security.SecureRandom 6 | import java.util.* 7 | 8 | class ValueGenerator { 9 | 10 | private var previousTimeMillis = System.currentTimeMillis() 11 | private var counter = 0L 12 | 13 | fun generateNextID(): Long { 14 | val currentTimeMillis = System.currentTimeMillis() 15 | counter = if (currentTimeMillis == previousTimeMillis) counter + 1L and 1048575L else 0L 16 | previousTimeMillis = currentTimeMillis 17 | val timeComponent = currentTimeMillis and 8796093022207L shl 20 18 | return timeComponent or counter 19 | } 20 | 21 | fun generateReceiptID(): String{ 22 | val uuid = UUID.randomUUID() 23 | val l: Long = ByteBuffer.wrap(uuid.toString().toByteArray()).long 24 | return l.toString(Character.MAX_RADIX) 25 | } 26 | 27 | fun generateCode(length: Int): Long { 28 | val ranGen = SecureRandom() 29 | val code = StringBuilder() 30 | for (i in 0..length){ 31 | code.append(ranGen.nextInt(9)) 32 | } 33 | return code.toString().toLong() 34 | } 35 | 36 | fun originalTransactionIDGen(): String { 37 | val uuid = UUID.randomUUID() 38 | val randomUUIDString = uuid.toString() 39 | return java.lang.StringBuilder().append("TERMINAL_TRANSACTION_ID=").append(randomUUIDString) 40 | .toString() 41 | } 42 | 43 | fun reversalOriginalDataElement( 44 | originalMessageType: String, 45 | originalSTAN: String, 46 | originalTransmissionDateTime: String, 47 | originalAcquirerInstitutionId: String, 48 | originalForwardingInstitutionId: String): String { 49 | val newOriginalAcquirerInstitutionId: String = StringUtil.rightPad('0', 11, originalAcquirerInstitutionId) 50 | val newOriginalForwardingInstitutionId = StringUtil.rightPad('0', 11, originalForwardingInstitutionId) 51 | return java.lang.StringBuilder() 52 | .append(originalMessageType) 53 | .append(originalSTAN) 54 | .append(originalTransmissionDateTime) 55 | .append(newOriginalAcquirerInstitutionId) 56 | .append(newOriginalForwardingInstitutionId) 57 | .toString() 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/interfaces/SocketHandler.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.interfaces 2 | 3 | import kotlin.Throws 4 | import com.subsidian.emvcardmanager.exceptions.ISOClientException 5 | import com.subsidian.emvcardmanager.handlers.SSLHandler 6 | import java.io.IOException 7 | import java.net.SocketException 8 | import java.nio.ByteBuffer 9 | 10 | /** 11 | *

Socket Handler Interface

12 | * Its responsible about initializing socket and connection to ISO switch 13 | * @author subsidian 14 | */ 15 | interface SocketHandler { 16 | /** 17 | * Initialize SSL connection to switch 18 | * @param host IP address of switch 19 | * @param port Switch port number 20 | * @param isoClientEventListener Event listener for dispatch state of operation 21 | * @param sslHandler Implementation of [SSLHandler] for handling ssl handshakes 22 | * @throws ISOClientException 23 | */ 24 | @Throws(ISOClientException::class) 25 | fun init( 26 | host: String, 27 | port: Int, 28 | isoClientEventListener: ISOClientEventListener?, 29 | sslHandler: SSLHandler? 30 | ) 31 | 32 | /** 33 | * Initialize NONE SSL connection to switch 34 | * @param host IP address of switch 35 | * @param port Switch port number 36 | * @param isoClientEventListener Event listener for dispatch state of operation 37 | * @throws IOException 38 | */ 39 | @Throws(IOException::class) 40 | fun init(host: String, port: Int, isoClientEventListener: ISOClientEventListener?) 41 | 42 | /** 43 | * Send message in sync way and return result 44 | * @param buffer buffer for sending 45 | * @param length length of message length 46 | * @return response buffer from message 47 | * @throws IOException 48 | * @throws ISOClientException 49 | */ 50 | @Throws(IOException::class, ISOClientException::class) 51 | fun sendMessageSync(buffer: ByteBuffer, length: Int): ByteArray? 52 | 53 | /** 54 | * Close current socket 55 | */ 56 | fun close() 57 | 58 | /** 59 | * Set waiting time for take a response from switch 60 | * @param readTimeout time out in milliseconds 61 | * @throws SocketException 62 | */ 63 | @Throws(SocketException::class) 64 | fun setReadTimeout(readTimeout: Int) 65 | 66 | /** 67 | * Check socket already connected to the host. 68 | * @return true if is connected 69 | */ 70 | fun isConnected(): Boolean 71 | 72 | /** 73 | * Check if socket is closed. 74 | * @return true if socket already closed 75 | */ 76 | fun isClosed(): Boolean 77 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/utils/AssetUtil.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.utils 2 | 3 | import android.content.Context 4 | import android.content.res.AssetManager 5 | import java.io.IOException 6 | import java.io.InputStream 7 | import java.lang.Exception 8 | 9 | object AssetUtil { 10 | fun getFilesArrayFromAssets(context: Context, path: String): Array? { 11 | val resources = context.resources 12 | val assetManager = resources.assets 13 | var files: Array? = null 14 | try { 15 | files = assetManager.list(path) 16 | } catch (e: IOException) { 17 | e.printStackTrace() 18 | } 19 | if (files != null) { 20 | for (i in files.indices) { 21 | files[i] = path + "/" + files[i] 22 | } 23 | } 24 | return files 25 | } 26 | 27 | fun getFromAssets(path: String?): ByteArray? { 28 | var `in`: InputStream? = null 29 | try { 30 | `in` = AssetUtil::class.java.classLoader.getResource("PACKAGER/NIBSS_PACKAGER.xml") 31 | .openStream() 32 | //获取文件的字节数 33 | val lenght = `in`.available() 34 | //创建byte数组 35 | val buffer = ByteArray(lenght) 36 | //将文件中的数据读到byte数组中 37 | `in`.read(buffer) 38 | return buffer 39 | } catch (e: Exception) { 40 | e.printStackTrace() 41 | } finally { 42 | if (`in` != null) try { 43 | `in`.close() 44 | } catch (e: IOException) { 45 | e.printStackTrace() 46 | } 47 | } 48 | return null 49 | } 50 | 51 | fun getFromAssets(context: Context, fileName: String?): ByteArray? { 52 | var `in`: InputStream? = null 53 | try { 54 | `in` = context.resources.assets.open(fileName!!) 55 | val lenght = `in`.available() 56 | //创建byte数组 57 | val buffer = ByteArray(lenght) 58 | `in`.read(buffer) 59 | return buffer 60 | } catch (e: Exception) { 61 | e.printStackTrace() 62 | } finally { 63 | if (`in` != null) try { 64 | `in`.close() 65 | } catch (e: IOException) { 66 | e.printStackTrace() 67 | } 68 | } 69 | return null 70 | } 71 | 72 | fun getInputStreamFromAssets(context: Context, fileName: String?): InputStream? { 73 | var `in`: InputStream? = null 74 | try { 75 | `in` = context.resources.assets.open(fileName!!) 76 | val lenght = `in`.available() 77 | return `in` 78 | } catch (e: Exception) { 79 | e.printStackTrace() 80 | } finally { 81 | if (`in` != null) try { 82 | `in`.close() 83 | } catch (e: IOException) { 84 | e.printStackTrace() 85 | } 86 | } 87 | return null 88 | } 89 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/transactions/ZMKRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.builders.transactions 2 | 3 | import com.solab.iso8583.IsoMessage 4 | import com.solab.iso8583.MessageFactory 5 | import com.subsidian.emvcardmanager.entities.ISOData 6 | import com.subsidian.emvcardmanager.enums.ISOMessageType 7 | import com.subsidian.emvcardmanager.utils.StringUtil 8 | 9 | object ZMKRequestBuilder { 10 | 11 | fun build (isoData: ISOData, messageFactory: MessageFactory): IsoMessage { 12 | val type: Int = ISOMessageType._0800.value.toInt(16) 13 | val message: IsoMessage = messageFactory.newMessage(type) 14 | val templ: IsoMessage = messageFactory.getMessageTemplate(type) 15 | /** Set ProcCode **/ 16 | if (isoData.processingCode != null && !StringUtil.isEmpty(isoData.processingCode)) { 17 | message.setValue( 18 | 3, 19 | isoData.processingCode, 20 | templ.getField(3).type, 21 | templ.getField(3).length 22 | ) 23 | } 24 | /** Set Transmission Date Time **/ 25 | if (isoData.transmissionDateTime != null && !StringUtil.isEmpty(isoData.transmissionDateTime)) { 26 | message.setValue( 27 | 7, 28 | isoData.transmissionDateTime, 29 | templ.getField(7).type, 30 | templ.getField(7).length 31 | ) 32 | } 33 | /** Set STAN **/ 34 | if (isoData.systemTraceAuditNumber != null && !StringUtil.isEmpty(isoData.systemTraceAuditNumber)) { 35 | message.setValue( 36 | 11, 37 | isoData.systemTraceAuditNumber, 38 | templ.getField(11).type, 39 | templ.getField(11).length 40 | ) 41 | } 42 | /** Set Local Time **/ 43 | if (isoData.localTime != null && !StringUtil.isEmpty(isoData.localTime)) { 44 | message.setValue( 45 | 12, 46 | isoData.localTime, 47 | templ.getField(12).type, 48 | templ.getField(12).length 49 | ) 50 | } 51 | /** Set Local Date **/ 52 | if (isoData.localDate != null && !StringUtil.isEmpty(isoData.localDate)) { 53 | message.setValue( 54 | 13, 55 | isoData.localDate, 56 | templ.getField(13).type, 57 | templ.getField(13).length 58 | ) 59 | } 60 | /** Set Card Acceptor Terminal Code or Terminal ID **/ 61 | if (isoData.cardAcceptorTerminalId != null && !StringUtil.isEmpty(isoData.cardAcceptorTerminalId)) { 62 | message.setValue( 63 | 41, 64 | isoData.cardAcceptorTerminalId, 65 | templ.getField(41).type, 66 | templ.getField(41).length 67 | ) 68 | } 69 | return message 70 | } 71 | 72 | } -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/transactions/TMKRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.builders.transactions 2 | 3 | import com.solab.iso8583.IsoMessage 4 | import com.solab.iso8583.MessageFactory 5 | import com.subsidian.emvcardmanager.entities.ISOData 6 | import com.subsidian.emvcardmanager.entities.ISOMessage 7 | import com.subsidian.emvcardmanager.enums.ISOMessageType 8 | import com.subsidian.emvcardmanager.utils.StringUtil 9 | 10 | object TMKRequestBuilder { 11 | 12 | fun build (isoData: ISOData, messageFactory: MessageFactory): ISOMessage { 13 | val type: Int = ISOMessageType._0800.value.toInt(16) 14 | val message: IsoMessage = messageFactory.newMessage(type) 15 | val templ: IsoMessage = messageFactory.getMessageTemplate(type) 16 | /** Set ProcCode **/ 17 | if (isoData.processingCode != null && !StringUtil.isEmpty(isoData.processingCode)) { 18 | message.setValue( 19 | 3, 20 | isoData.processingCode, 21 | templ.getField(3).type, 22 | templ.getField(3).length 23 | ) 24 | } 25 | /** Set Transmission Date Time **/ 26 | if (isoData.transmissionDateTime != null && !StringUtil.isEmpty(isoData.transmissionDateTime)) { 27 | message.setValue( 28 | 7, 29 | isoData.transmissionDateTime, 30 | templ.getField(7).type, 31 | templ.getField(7).length 32 | ) 33 | } 34 | /** Set STAN **/ 35 | if (isoData.systemTraceAuditNumber != null && !StringUtil.isEmpty(isoData.systemTraceAuditNumber)) { 36 | message.setValue( 37 | 11, 38 | isoData.systemTraceAuditNumber, 39 | templ.getField(11).type, 40 | templ.getField(11).length 41 | ) 42 | } 43 | /** Set Local Time **/ 44 | if (isoData.localTime != null && !StringUtil.isEmpty(isoData.localTime)) { 45 | message.setValue( 46 | 12, 47 | isoData.localTime, 48 | templ.getField(12).type, 49 | templ.getField(12).length 50 | ) 51 | } 52 | /** Set Local Date **/ 53 | if (isoData.localDate != null && !StringUtil.isEmpty(isoData.localDate)) { 54 | message.setValue( 55 | 13, 56 | isoData.localDate, 57 | templ.getField(13).type, 58 | templ.getField(13).length 59 | ) 60 | } 61 | /** Set Card Acceptor Terminal Code or Terminal ID **/ 62 | if (isoData.cardAcceptorTerminalId != null && !StringUtil.isEmpty(isoData.cardAcceptorTerminalId)) { 63 | message.setValue( 64 | 41, 65 | isoData.cardAcceptorTerminalId, 66 | templ.getField(41).type, 67 | templ.getField(41).length 68 | ) 69 | } 70 | return ISOMessage(message) 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/transactions/TPKRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.builders.transactions 2 | 3 | import com.solab.iso8583.IsoMessage 4 | import com.solab.iso8583.MessageFactory 5 | import com.subsidian.emvcardmanager.entities.ISOData 6 | import com.subsidian.emvcardmanager.entities.ISOMessage 7 | import com.subsidian.emvcardmanager.enums.ISOMessageType 8 | import com.subsidian.emvcardmanager.utils.StringUtil 9 | 10 | object TPKRequestBuilder { 11 | 12 | fun build (isoData: ISOData, messageFactory: MessageFactory): ISOMessage { 13 | val type: Int = ISOMessageType._0800.value.toInt(16) 14 | val message: IsoMessage = messageFactory.newMessage(type) 15 | val templ: IsoMessage = messageFactory.getMessageTemplate(type) 16 | /** Set ProcCode **/ 17 | if (isoData.processingCode != null && !StringUtil.isEmpty(isoData.processingCode)) { 18 | message.setValue( 19 | 3, 20 | isoData.processingCode, 21 | templ.getField(3).type, 22 | templ.getField(3).length 23 | ) 24 | } 25 | /** Set Transmission Date Time **/ 26 | if (isoData.transmissionDateTime != null && !StringUtil.isEmpty(isoData.transmissionDateTime)) { 27 | message.setValue( 28 | 7, 29 | isoData.transmissionDateTime, 30 | templ.getField(7).type, 31 | templ.getField(7).length 32 | ) 33 | } 34 | /** Set STAN **/ 35 | if (isoData.systemTraceAuditNumber != null && !StringUtil.isEmpty(isoData.systemTraceAuditNumber)) { 36 | message.setValue( 37 | 11, 38 | isoData.systemTraceAuditNumber, 39 | templ.getField(11).type, 40 | templ.getField(11).length 41 | ) 42 | } 43 | /** Set Local Time **/ 44 | if (isoData.localTime != null && !StringUtil.isEmpty(isoData.localTime)) { 45 | message.setValue( 46 | 12, 47 | isoData.localTime, 48 | templ.getField(12).type, 49 | templ.getField(12).length 50 | ) 51 | } 52 | /** Set Local Date **/ 53 | if (isoData.localDate != null && !StringUtil.isEmpty(isoData.localDate)) { 54 | message.setValue( 55 | 13, 56 | isoData.localDate, 57 | templ.getField(13).type, 58 | templ.getField(13).length 59 | ) 60 | } 61 | /** Set Card Acceptor Terminal Code or Terminal ID **/ 62 | if (isoData.cardAcceptorTerminalId != null && !StringUtil.isEmpty(isoData.cardAcceptorTerminalId)) { 63 | message.setValue( 64 | 41, 65 | isoData.cardAcceptorTerminalId, 66 | templ.getField(41).type, 67 | templ.getField(41).length 68 | ) 69 | } 70 | return ISOMessage(message) 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/transactions/TSKRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.builders.transactions 2 | 3 | import com.solab.iso8583.IsoMessage 4 | import com.solab.iso8583.MessageFactory 5 | import com.subsidian.emvcardmanager.entities.ISOData 6 | import com.subsidian.emvcardmanager.entities.ISOMessage 7 | import com.subsidian.emvcardmanager.enums.ISOMessageType 8 | import com.subsidian.emvcardmanager.utils.StringUtil 9 | 10 | object TSKRequestBuilder { 11 | 12 | fun build (isoData: ISOData, messageFactory: MessageFactory): ISOMessage { 13 | val type: Int = ISOMessageType._0800.value.toInt(16) 14 | val message: IsoMessage = messageFactory.newMessage(type) 15 | val templ: IsoMessage = messageFactory.getMessageTemplate(type) 16 | /** Set ProcCode **/ 17 | if (isoData.processingCode != null && !StringUtil.isEmpty(isoData.processingCode)) { 18 | message.setValue( 19 | 3, 20 | isoData.processingCode, 21 | templ.getField(3).type, 22 | templ.getField(3).length 23 | ) 24 | } 25 | /** Set Transmission Date Time **/ 26 | if (isoData.transmissionDateTime != null && !StringUtil.isEmpty(isoData.transmissionDateTime)) { 27 | message.setValue( 28 | 7, 29 | isoData.transmissionDateTime, 30 | templ.getField(7).type, 31 | templ.getField(7).length 32 | ) 33 | } 34 | /** Set STAN **/ 35 | if (isoData.systemTraceAuditNumber != null && !StringUtil.isEmpty(isoData.systemTraceAuditNumber)) { 36 | message.setValue( 37 | 11, 38 | isoData.systemTraceAuditNumber, 39 | templ.getField(11).type, 40 | templ.getField(11).length 41 | ) 42 | } 43 | /** Set Local Time **/ 44 | if (isoData.localTime != null && !StringUtil.isEmpty(isoData.localTime)) { 45 | message.setValue( 46 | 12, 47 | isoData.localTime, 48 | templ.getField(12).type, 49 | templ.getField(12).length 50 | ) 51 | } 52 | /** Set Local Date **/ 53 | if (isoData.localDate != null && !StringUtil.isEmpty(isoData.localDate)) { 54 | message.setValue( 55 | 13, 56 | isoData.localDate, 57 | templ.getField(13).type, 58 | templ.getField(13).length 59 | ) 60 | } 61 | /** Set Card Acceptor Terminal Code or Terminal ID **/ 62 | if (isoData.cardAcceptorTerminalId != null && !StringUtil.isEmpty(isoData.cardAcceptorTerminalId)) { 63 | message.setValue( 64 | 41, 65 | isoData.cardAcceptorTerminalId, 66 | templ.getField(41).type, 67 | templ.getField(41).length 68 | ) 69 | } 70 | return ISOMessage(message) 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/entities/ISOMessageContainer.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.entities 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import com.google.gson.annotations.Expose 5 | 6 | class ISOMessageContainer { 7 | @SerializedName("Field_2") 8 | @Expose 9 | var field2: String? = null 10 | 11 | @SerializedName("Field_3") 12 | @Expose 13 | var field3: String? = null 14 | 15 | @SerializedName("Field_4") 16 | @Expose 17 | var field4: String? = null 18 | 19 | @SerializedName("Field_7") 20 | @Expose 21 | var field7: String? = null 22 | 23 | @SerializedName("Field_11") 24 | @Expose 25 | var field11: String? = null 26 | 27 | @SerializedName("Field_12") 28 | @Expose 29 | var field12: String? = null 30 | 31 | @SerializedName("Field_13") 32 | @Expose 33 | var field13: String? = null 34 | 35 | @SerializedName("Field_14") 36 | @Expose 37 | var field14: String? = null 38 | 39 | @SerializedName("Field_18") 40 | @Expose 41 | var field18: String? = null 42 | 43 | @SerializedName("Field_22") 44 | @Expose 45 | var field22: String? = null 46 | 47 | @SerializedName("Field_23") 48 | @Expose 49 | var field23: String? = null 50 | 51 | @SerializedName("Field_25") 52 | @Expose 53 | var field25: String? = null 54 | 55 | @SerializedName("Field_26") 56 | @Expose 57 | var field26: String? = null 58 | 59 | @SerializedName("Field_28") 60 | @Expose 61 | var field28: String? = null 62 | 63 | @SerializedName("Field_32") 64 | @Expose 65 | var field32: String? = null 66 | 67 | @SerializedName("Field_33") 68 | @Expose 69 | var field33: String? = null 70 | 71 | @SerializedName("Field_35") 72 | @Expose 73 | var field35: String? = null 74 | 75 | @SerializedName("Field_37") 76 | @Expose 77 | var field37: String? = null 78 | 79 | @SerializedName("Field_39") 80 | @Expose 81 | var field39: String? = null 82 | 83 | @SerializedName("Field_40") 84 | @Expose 85 | var field40: String? = null 86 | 87 | @SerializedName("Field_41") 88 | @Expose 89 | var field41: String? = null 90 | 91 | @SerializedName("Field_42") 92 | @Expose 93 | var field42: String? = null 94 | 95 | @SerializedName("Field_43") 96 | @Expose 97 | var field43: String? = null 98 | 99 | @SerializedName("Field_49") 100 | @Expose 101 | var field49: String? = null 102 | 103 | @SerializedName("Field_53") 104 | @Expose 105 | var field53: String? = null 106 | 107 | @SerializedName("Field_55") 108 | @Expose 109 | var field55: String? = null 110 | 111 | @SerializedName("Field_63") 112 | @Expose 113 | var field63: String? = null 114 | 115 | @SerializedName("Field_64") 116 | @Expose 117 | var field64: String? = null 118 | 119 | @SerializedName("Field_123") 120 | @Expose 121 | var field123: String? = null 122 | 123 | @SerializedName("Field_128") 124 | @Expose 125 | var field128: String? = null 126 | } -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 30sp 4 | 300dp 5 | 45dp 6 | 110dp 7 | 20dp 8 | 16dp 9 | 16dp 10 | 5dp 11 | 12 | 12sp 13 | 14sp 14 | 16sp 15 | 20sp 16 | 24sp 17 | 32sp 18 | 40sp 19 | 48sp 20 | 56sp 21 | 64sp 22 | 72sp 23 | 80sp 24 | 25 | 0dp 26 | 1dp 27 | 2dp 28 | 3dp 29 | 4dp 30 | 5dp 31 | 8dp 32 | 15dp 33 | 16dp 34 | 24dp 35 | 32dp 36 | 40dp 37 | 48dp 38 | 50dp 39 | 56dp 40 | 64dp 41 | 72dp 42 | 80dp 43 | 90dp 44 | 100dp 45 | 110dp 46 | 120dp 47 | 130dp 48 | 140dp 49 | 150dp 50 | 160dp 51 | 170dp 52 | 180dp 53 | 160dp 54 | 160dp 55 | 260dp 56 | 300dp 57 | 350dp 58 | 360dp 59 | 400dp 60 | 450dp 61 | 62 | 7dp 63 | 64 | 65 | -1 66 | -2 67 | 68 | 69 | 16dp 70 | @dimen/wrap_content 71 | 7dp 72 | 5dp 73 | 48dp 74 | 30dp 75 | 85dp 76 | 12sp 77 | 90dp 78 | -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/TransactionUtilities.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.security.ValueGenerator 4 | import com.subsidian.emvcardmanager.utils.StringUtil 5 | import com.subsidian.emvcardmanager.utils.TimeUtil 6 | import java.util.* 7 | 8 | class TransactionUtilities { 9 | 10 | private val timeUtil = TimeUtil() 11 | private val now = Date(System.currentTimeMillis()) 12 | 13 | fun primaryAccountNumber() = "4761739001010010" 14 | 15 | fun processingCode() = "000000" 16 | 17 | fun transactionAmount() = "000000000253" 18 | 19 | fun transmissionDateTime() = timeUtil.getDateTimeMMddhhmmss(now) 20 | 21 | fun settlementConversionRate() = "" 22 | 23 | fun systemTraceAuditNumber() = StringUtil.leftPadding('0', 6, ValueGenerator().generateCode(5).toString()) 24 | 25 | fun localTime() = timeUtil.getTimehhmmss(now) 26 | 27 | fun localDate() = timeUtil.getDateMMdd(now) 28 | 29 | fun expirationDate() = "2212" 30 | 31 | fun settlementDate() = "" 32 | 33 | fun conversionDate() = "" 34 | 35 | fun merchantType() = "1520" 36 | 37 | fun posEntryMode() = "071" 38 | 39 | fun cardSequenceNumber() = "001" 40 | 41 | fun posConditionCode() = "00" 42 | 43 | fun posPinCaptureCode() = "04" 44 | 45 | fun transactionFeeAmount() = "C00000000" 46 | 47 | fun settlementAmount() = "" 48 | 49 | fun transactionProcessingFeeAmount() = "" 50 | 51 | fun settleProcessingFeeAmount() = "" 52 | 53 | fun acquiringInstitutionId() = "00000000000" 54 | 55 | fun forwardingInstitutionId() = "" 56 | 57 | fun trackTwoData() = "4761739001010010D22122011143844489" 58 | 59 | fun retrievalReferenceNumber() = StringUtil.leftPadding('0', 12, ValueGenerator().generateCode(12).toString()) 60 | 61 | fun authorizationIdResponse() = "" 62 | 63 | fun responseCode() = "" 64 | 65 | fun serviceRestrictionCode() = "201" 66 | 67 | fun cardAcceptorTerminalId() = "20584535" 68 | 69 | fun cardAcceptorIdCode() = "2044LA000017579" 70 | 71 | fun cardAcceptorNameLocation() = "Office LA LANG" 72 | 73 | fun additionalResponseData() = "" 74 | 75 | fun additionalData() = "" 76 | 77 | fun transactionCurrencyCode() = "566" 78 | 79 | fun settlementCurrencyCode() = "" 80 | 81 | fun pinData() = "" 82 | 83 | fun securityRelatedControlInformation() = "" 84 | 85 | fun additionalAmounts() = "" 86 | 87 | fun integratedCircuitCardData() = "9F260854F3AF21B4227A379F2701009F100706010A038000009F37040B859F619F3602070E950500000000009A032106109C01209F0201305F2A03353636820220009F1A033536369F03060000000000009F3303E090C89F350057114761739001010010D221220111438444895A0847617390010100105F3401018407A00000000310105F2015564953412044454249542F43415244484F4C444552" 88 | 89 | fun messageResponseCode() = "" 90 | 91 | fun authorizingAgentId() = "" 92 | 93 | fun transportEchoData() = "" 94 | 95 | fun paymentInformation() = "" 96 | 97 | fun managementDataOnePrivate() = "" 98 | 99 | fun managementDataTwoPrivate() = "" 100 | 101 | fun primaryMessageHashValue() = "B6423D83EDE27AE98A05D78063381AE3B895CB59BE7B3D0C3E17BD20BCAA668C" 102 | 103 | fun extendedPaymentCode() = "" 104 | 105 | fun originalDataElement() = "" 106 | 107 | fun replacementAmount() = "" 108 | 109 | fun payee() = "" 110 | 111 | fun receivingInstitutionId() = "" 112 | 113 | fun accountIdentification1() = "" 114 | 115 | fun accountIdentification2() = "" 116 | 117 | fun posDataCode() = "911101513344101" 118 | 119 | fun nearFieldCommunicationData() = "" 120 | 121 | fun secondaryMessageHashValue() = "B6423D83EDE27AE98A05D78063381AE3B895CB59BE7B3D0C3E17BD20BCAA668C" 122 | } -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/ui/custom/KeyboardView.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.ui.custom 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.view.View 6 | import android.widget.Button 7 | import android.widget.FrameLayout 8 | import android.widget.TextView 9 | import androidx.annotation.IdRes 10 | import com.subsidian.emvcardsdkdemo.R 11 | import java.lang.Exception 12 | 13 | class KeyboardView : FrameLayout, View.OnClickListener { 14 | 15 | private var mCurrencyField: CurrencyEditText? = null 16 | private var mStoreButton: Button? = null 17 | 18 | val inputText: String 19 | get() = mCurrencyField!!.text.toString() 20 | 21 | constructor(context: Context) : super(context) { 22 | init() 23 | } 24 | 25 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { 26 | init() 27 | } 28 | 29 | constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { 30 | init() 31 | } 32 | 33 | private fun init() { 34 | View.inflate(context, R.layout.keyboard, this) 35 | initViews() 36 | } 37 | 38 | private fun initViews() { 39 | mCurrencyField = layoutView(R.id.currency_field) 40 | mCurrencyField!!.setText("0.00") 41 | mStoreButton = layoutView(R.id.store_btn) 42 | layoutView(R.id.t9_key_0).setOnClickListener(this) 43 | layoutView(R.id.t9_key_1).setOnClickListener(this) 44 | layoutView(R.id.t9_key_2).setOnClickListener(this) 45 | layoutView(R.id.t9_key_3).setOnClickListener(this) 46 | layoutView(R.id.t9_key_4).setOnClickListener(this) 47 | layoutView(R.id.t9_key_5).setOnClickListener(this) 48 | layoutView(R.id.t9_key_6).setOnClickListener(this) 49 | layoutView(R.id.t9_key_7).setOnClickListener(this) 50 | layoutView(R.id.t9_key_8).setOnClickListener(this) 51 | layoutView(R.id.t9_key_9).setOnClickListener(this) 52 | layoutView(R.id.t9_key_clear).setOnClickListener(this) 53 | layoutView(R.id.t9_key_backspace).setOnClickListener(this) 54 | layoutView(R.id.t9_key_add_sale).setOnClickListener(this) 55 | } 56 | 57 | override fun onClick(v: View) { 58 | // handle number button click 59 | if (v.tag != null && "number_button" == v.tag) { 60 | mCurrencyField!!.append((v as TextView).text) 61 | return 62 | } 63 | when (v.id) { 64 | R.id.t9_key_clear -> { 65 | // handle clear button 66 | // clearCurrentAmount() 67 | } 68 | R.id.t9_key_backspace -> { // handle backspace button 69 | // delete one character 70 | val editable = mCurrencyField!!.text 71 | val charCount = editable!!.length 72 | if (charCount > 0) { 73 | editable.delete(charCount - 1, charCount) 74 | } 75 | } 76 | } 77 | } 78 | 79 | protected fun layoutView(@IdRes id: Int): T { 80 | return super.findViewById(id) as T 81 | } 82 | 83 | fun getButtonView(@IdRes id: Int): View { 84 | return findViewById(id) 85 | } 86 | 87 | fun clearCurrentAmount(value: String?) { 88 | val editable = mCurrencyField!!.text 89 | try { 90 | val charCount = editable!!.length 91 | if (charCount > 0) { 92 | 93 | } 94 | } catch (e: Exception) { 95 | e.printStackTrace() 96 | } 97 | } 98 | 99 | fun getCurrencyTextView(): CurrencyEditText { 100 | return mCurrencyField!! 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/PurchaseRequest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.builders.ISODataBuilder 4 | import com.subsidian.emvcardmanager.entities.ISOData 5 | import com.subsidian.emvcardmanager.enums.ISOAccountType 6 | import com.subsidian.emvcardmanager.enums.ISOMessageType 7 | import com.subsidian.emvcardmanager.enums.ISOTransactionType 8 | 9 | object PurchaseRequest { 10 | 11 | private val transactionUtilities = TransactionUtilities() 12 | 13 | fun build (): ISOData { 14 | val transactionType = ISOTransactionType.PURCHASE_TRANSACTION_TYPE 15 | val accountType = ISOAccountType.DEFAULT_ACCTOUNT_TYPE 16 | return ISODataBuilder.Builder() 17 | .messageType(ISOMessageType._0200.value) 18 | .primaryAccountNumber(transactionUtilities.primaryAccountNumber()) 19 | .processingCode(transactionType.value.plus(accountType.value).plus("00")) 20 | .transactionAmount(transactionUtilities.transactionAmount()) 21 | .transmissionDateTime(transactionUtilities.transmissionDateTime()) 22 | .settlementConversionRate("") 23 | .systemTraceAuditNumber(transactionUtilities.systemTraceAuditNumber()) 24 | .localTime(transactionUtilities.localTime()) 25 | .localDate(transactionUtilities.localDate()) 26 | .expirationDate(transactionUtilities.expirationDate()) 27 | .settlementDate("") 28 | .conversionDate("") 29 | .merchantType(transactionUtilities.merchantType()) 30 | .posEntryMode(transactionUtilities.posEntryMode()) 31 | .cardSequenceNumber(transactionUtilities.cardSequenceNumber()) 32 | .posConditionCode(transactionUtilities.posConditionCode()) 33 | .posPinCaptureCode(transactionUtilities.posPinCaptureCode()) 34 | .transactionFeeAmount(transactionUtilities.transactionFeeAmount()) 35 | .settlementAmount("") 36 | .transactionProcessingFeeAmount("") 37 | .settleProcessingFeeAmount("") 38 | .acquiringInstitutionId(transactionUtilities.acquiringInstitutionId()) 39 | .forwardingInstitutionId("") 40 | .trackTwoData(transactionUtilities.trackTwoData()) 41 | .retrievalReferenceNumber(transactionUtilities.retrievalReferenceNumber()) 42 | .authorizationIdResponse("") 43 | .responseCode("") 44 | .serviceRestrictionCode(transactionUtilities.serviceRestrictionCode()) 45 | .cardAcceptorTerminalId(transactionUtilities.cardAcceptorTerminalId()) 46 | .cardAcceptorIdCode(transactionUtilities.cardAcceptorIdCode()) 47 | .cardAcceptorNameLocation(transactionUtilities.cardAcceptorNameLocation()) 48 | .additionalResponseData("") 49 | .additionalData("") 50 | .transactionCurrencyCode(transactionUtilities.transactionCurrencyCode()) 51 | .settlementCurrencyCode("") 52 | .pinData("") 53 | .securityRelatedControlInformation("") 54 | .additionalAmounts("") 55 | .integratedCircuitCardData(transactionUtilities.integratedCircuitCardData()) 56 | .messageReasonCode("") 57 | .authorizingAgentId("") 58 | .transportEchoData("") 59 | .paymentInformation("") 60 | .managementDataOnePrivate("") 61 | .managementDataTwoPrivate("") 62 | .primaryMessageHashValue("") 63 | .extendedPaymentCode("") 64 | .originalDataElement("") 65 | .replacementAmount("") 66 | .payee("") 67 | .receivingInstitutionId("") 68 | .accountIdentification1("") 69 | .accountIdentification2("") 70 | .posDataCode(transactionUtilities.posDataCode()) 71 | .nearFieldCommunicationData("") 72 | .secondaryMessageHashValue(transactionUtilities.secondaryMessageHashValue()) 73 | .build() 74 | } 75 | 76 | } -------------------------------------------------------------------------------- /app/src/main/java/com/subsidian/emvcardsdkdemo/utils/transaction/ReversalRequest.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardsdkdemo.utils.transaction 2 | 3 | import com.subsidian.emvcardmanager.builders.ISODataBuilder 4 | import com.subsidian.emvcardmanager.entities.ISOData 5 | import com.subsidian.emvcardmanager.enums.ISOAccountType 6 | import com.subsidian.emvcardmanager.enums.ISOMessageType 7 | import com.subsidian.emvcardmanager.enums.ISOTransactionType 8 | import com.subsidian.emvcardmanager.security.ValueGenerator 9 | 10 | object ReversalRequest { 11 | 12 | private val transactionUtilities = TransactionUtilities() 13 | 14 | fun build (): ISOData { 15 | val transactionType = ISOTransactionType.PURCHASE_TRANSACTION_TYPE 16 | val accountType = ISOAccountType.DEFAULT_ACCTOUNT_TYPE 17 | return ISODataBuilder.Builder() 18 | .messageType(ISOMessageType._0420.value) // set to 0421 if it's a repeat 19 | .primaryAccountNumber(transactionUtilities.primaryAccountNumber()) 20 | .processingCode(transactionType.value.plus(accountType.value).plus("00")) 21 | .transactionAmount(transactionUtilities.transactionAmount()) 22 | .transmissionDateTime(transactionUtilities.transmissionDateTime()) 23 | .settlementConversionRate("") 24 | .systemTraceAuditNumber(transactionUtilities.systemTraceAuditNumber()) 25 | .localTime(transactionUtilities.localTime()) 26 | .localDate(transactionUtilities.localDate()) 27 | .expirationDate(transactionUtilities.expirationDate()) 28 | .settlementDate("") 29 | .conversionDate("") 30 | .merchantType(transactionUtilities.merchantType()) 31 | .posEntryMode(transactionUtilities.posEntryMode()) 32 | .cardSequenceNumber(transactionUtilities.cardSequenceNumber()) 33 | .posConditionCode(transactionUtilities.posConditionCode()) 34 | .posPinCaptureCode(transactionUtilities.posPinCaptureCode()) 35 | .transactionFeeAmount(transactionUtilities.transactionFeeAmount()) 36 | .settlementAmount("") 37 | .transactionProcessingFeeAmount("") 38 | .settleProcessingFeeAmount("") 39 | .acquiringInstitutionId(transactionUtilities.acquiringInstitutionId()) 40 | .forwardingInstitutionId("") 41 | .trackTwoData(transactionUtilities.trackTwoData()) 42 | .retrievalReferenceNumber(transactionUtilities.retrievalReferenceNumber()) 43 | .authorizationIdResponse("") 44 | .responseCode("") 45 | .serviceRestrictionCode(transactionUtilities.serviceRestrictionCode()) 46 | .cardAcceptorTerminalId(transactionUtilities.cardAcceptorTerminalId()) 47 | .cardAcceptorIdCode(transactionUtilities.cardAcceptorIdCode()) 48 | .cardAcceptorNameLocation(transactionUtilities.cardAcceptorNameLocation()) 49 | .additionalResponseData("") 50 | .additionalData("") 51 | .transactionCurrencyCode(transactionUtilities.transactionCurrencyCode()) 52 | .settlementCurrencyCode("") 53 | .pinData("") 54 | .securityRelatedControlInformation("") 55 | .additionalAmounts("") 56 | .integratedCircuitCardData("") 57 | .messageReasonCode("") 58 | .authorizingAgentId("") 59 | .transportEchoData("") 60 | .paymentInformation("") 61 | .managementDataOnePrivate("") 62 | .managementDataTwoPrivate("") 63 | .primaryMessageHashValue("") 64 | .extendedPaymentCode("") 65 | .originalDataElement(ValueGenerator() 66 | .reversalOriginalDataElement( 67 | ISOMessageType._0200.value, 68 | transactionUtilities.systemTraceAuditNumber(), 69 | transactionUtilities.transmissionDateTime(), 70 | transactionUtilities.acquiringInstitutionId(), 71 | transactionUtilities.forwardingInstitutionId() 72 | )) 73 | .replacementAmount("") 74 | .payee("") 75 | .receivingInstitutionId("") 76 | .accountIdentification1("") 77 | .accountIdentification2("") 78 | .posDataCode(transactionUtilities.posDataCode()) 79 | .nearFieldCommunicationData("") 80 | .secondaryMessageHashValue(transactionUtilities.secondaryMessageHashValue()) 81 | .build() 82 | } 83 | 84 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/transactions/IPEKEMVRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.builders.transactions 2 | 3 | import com.solab.iso8583.IsoMessage 4 | import com.solab.iso8583.MessageFactory 5 | import com.subsidian.emvcardmanager.entities.ISOData 6 | import com.subsidian.emvcardmanager.entities.ISOMessage 7 | import com.subsidian.emvcardmanager.enums.ISOMessageType 8 | import com.subsidian.emvcardmanager.utils.KeyUtil 9 | import com.subsidian.emvcardmanager.utils.StringUtil 10 | import java.lang.StringBuilder 11 | 12 | object IPEKEMVRequestBuilder { 13 | 14 | fun build (isoData: ISOData, messageFactory: MessageFactory, terminalSessionKey: String = ""): ISOMessage { 15 | val type: Int = ISOMessageType._0800.value.toInt(16) 16 | val message: IsoMessage = messageFactory.newMessage(type) 17 | val templ: IsoMessage = messageFactory.getMessageTemplate(type) 18 | /** Set ProcCode **/ 19 | if (isoData.processingCode != null && !StringUtil.isEmpty(isoData.processingCode)) { 20 | message.setValue( 21 | 3, 22 | isoData.processingCode, 23 | templ.getField(3).type, 24 | templ.getField(3).length 25 | ) 26 | } 27 | /** Set Transmission Date Time **/ 28 | if (isoData.transmissionDateTime != null && !StringUtil.isEmpty(isoData.transmissionDateTime)) { 29 | message.setValue( 30 | 7, 31 | isoData.transmissionDateTime, 32 | templ.getField(7).type, 33 | templ.getField(7).length 34 | ) 35 | } 36 | /** Set STAN **/ 37 | if (isoData.systemTraceAuditNumber != null && !StringUtil.isEmpty(isoData.systemTraceAuditNumber)) { 38 | message.setValue( 39 | 11, 40 | isoData.systemTraceAuditNumber, 41 | templ.getField(11).type, 42 | templ.getField(11).length 43 | ) 44 | } 45 | /** Set Local Time **/ 46 | if (isoData.localTime != null && !StringUtil.isEmpty(isoData.localTime)) { 47 | message.setValue( 48 | 12, 49 | isoData.localTime, 50 | templ.getField(12).type, 51 | templ.getField(12).length 52 | ) 53 | } 54 | /** Set Local Date **/ 55 | if (isoData.localDate != null && !StringUtil.isEmpty(isoData.localDate)) { 56 | message.setValue( 57 | 13, 58 | isoData.localDate, 59 | templ.getField(13).type, 60 | templ.getField(13).length 61 | ) 62 | } 63 | /** Set Card Acceptor Terminal Code or Terminal ID **/ 64 | if (isoData.cardAcceptorTerminalId != null && !StringUtil.isEmpty(isoData.cardAcceptorTerminalId)) { 65 | message.setValue( 66 | 41, 67 | isoData.cardAcceptorTerminalId, 68 | templ.getField(41).type, 69 | templ.getField(41).length 70 | ) 71 | } 72 | /** Set Primary Message Hash **/ 73 | if (!StringUtil.isEmpty(terminalSessionKey)) { 74 | message.setValue( 75 | 64, 76 | String(byteArrayOf(0x0)), 77 | templ.getField(64).type, 78 | templ.getField(64).length 79 | ) 80 | val bytes = message.writeData() 81 | val length = bytes.size 82 | val temp = ByteArray(length - 64) 83 | if (length >= 64) { 84 | System.arraycopy(bytes, 0, temp, 0, length - 64) 85 | } 86 | val hashValue: String = KeyUtil().getMac(terminalSessionKey, temp) 87 | message.setValue( 88 | 64, 89 | hashValue, 90 | templ.getField(64).type, 91 | templ.getField(64).length 92 | ) 93 | } else if (isoData.primaryMessageHashValue != null && !StringUtil.isEmpty(isoData.primaryMessageHashValue)){ 94 | message.setValue( 95 | 64, 96 | isoData.primaryMessageHashValue, 97 | templ.getField(64).type, 98 | templ.getField(64).length 99 | ) 100 | } 101 | return ISOMessage(message) 102 | } 103 | 104 | } -------------------------------------------------------------------------------- /emvcardmanager/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'kotlin-android' 4 | id 'kotlin-kapt' 5 | id 'kotlin-android-extensions' 6 | id 'maven-publish' 7 | } 8 | 9 | android { 10 | compileSdk 31 11 | 12 | defaultConfig { 13 | minSdk 22 14 | targetSdk 31 15 | versionCode 1 16 | versionName "1.0" 17 | 18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 19 | consumerProguardFiles "consumer-rules.pro" 20 | } 21 | 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | compileOptions { 29 | sourceCompatibility JavaVersion.VERSION_1_8 30 | targetCompatibility JavaVersion.VERSION_1_8 31 | } 32 | kotlinOptions { 33 | jvmTarget = '1.8' 34 | } 35 | 36 | buildTypes { 37 | debug { 38 | minifyEnabled false 39 | shrinkResources false 40 | zipAlignEnabled false 41 | debuggable true 42 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 43 | } 44 | release { 45 | minifyEnabled false 46 | shrinkResources false 47 | zipAlignEnabled false 48 | debuggable false 49 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 50 | } 51 | } 52 | } 53 | 54 | dependencies { 55 | implementation fileTree(include: ['*.jar'], dir: 'libs') 56 | implementation 'androidx.core:core-ktx:1.6.0' 57 | implementation 'androidx.appcompat:appcompat:1.3.1' 58 | implementation 'com.google.android.material:material:1.4.0' 59 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3") 60 | implementation 'com.google.code.gson:gson:2.8.8' 61 | testImplementation 'junit:junit:4+' 62 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 63 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 64 | } 65 | 66 | // https://proandroiddev.com/publishing-android-libraries-to-the-github-package-registry-part-1-7997be54ea5a 67 | def githubProperties = new Properties() 68 | // github.properties or local.properties 69 | githubProperties.load(new FileInputStream(rootProject.file("local.properties"))) //Set env variable GPR_USER & GPR_API_KEY if not adding a properties file 70 | 71 | def getVersionName = { -> 72 | return "0.0.1" // Replace with version Name 73 | } 74 | 75 | def getArtificatId = { -> 76 | return "emvcardmanager" // Replace with library name ID 77 | } 78 | 79 | publishing { 80 | publications { 81 | release(MavenPublication) { 82 | groupId 'com.subsidian' // Replace with group ID 83 | artifactId getArtificatId() 84 | version getVersionName() 85 | artifact("$buildDir/outputs/aar/${getArtificatId()}-release.aar") 86 | } 87 | 88 | debug(MavenPublication) { 89 | groupId 'com.subsidian' // Replace with group ID 90 | artifactId getArtificatId() 91 | version getVersionName() 92 | artifact("$buildDir/outputs/aar/${getArtificatId()}-release.aar") 93 | } 94 | } 95 | 96 | repositories { 97 | maven { 98 | name = "GitHubPackages" 99 | /** Configure path of your package repository on Github 100 | ** Replace GITHUB_USERID with your/organisation Github userID 101 | ** and REPOSITORY with the repository name on GitHub 102 | */ 103 | url = uri("https://maven.pkg.github.com/subsidian-ia/emv-card-manager") 104 | credentials { 105 | /** Create github.properties in root project folder file with 106 | ** gpr.usr=GITHUB_USER_ID & gpr.key=PERSONAL_ACCESS_TOKEN 107 | ** Set env variable GPR_USER & GPR_API_KEY if not adding a properties file**/ 108 | username = githubProperties['gpr.usr'] ?: System.getenv("GPR_USER") 109 | password = githubProperties['gpr.key'] ?: System.getenv("GPR_API_KEY") 110 | } 111 | } 112 | } 113 | } 114 | 115 | task cleanBuildPublishLocal(type: GradleBuild) { 116 | tasks = ['clean', 'build', 'publishToMavenLocal'] 117 | } 118 | 119 | task cleanBuildPublish(type: GradleBuild) { 120 | tasks = ['clean', 'build', 'publish'] 121 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/transactions/IPEKTrackTwoRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.builders.transactions 2 | 3 | import com.solab.iso8583.IsoMessage 4 | import com.solab.iso8583.MessageFactory 5 | import com.subsidian.emvcardmanager.entities.ISOData 6 | import com.subsidian.emvcardmanager.entities.ISOMessage 7 | import com.subsidian.emvcardmanager.enums.ISOMessageType 8 | import com.subsidian.emvcardmanager.utils.KeyUtil 9 | import com.subsidian.emvcardmanager.utils.StringUtil 10 | import java.lang.StringBuilder 11 | 12 | object IPEKTrackTwoRequestBuilder { 13 | 14 | fun build (isoData: ISOData, messageFactory: MessageFactory, terminalSessionKey: String = ""): ISOMessage { 15 | val type: Int = ISOMessageType._0800.value.toInt(16) 16 | val message: IsoMessage = messageFactory.newMessage(type) 17 | val templ: IsoMessage = messageFactory.getMessageTemplate(type) 18 | /** Set ProcCode **/ 19 | if (isoData.processingCode != null && !StringUtil.isEmpty(isoData.processingCode)) { 20 | message.setValue( 21 | 3, 22 | isoData.processingCode, 23 | templ.getField(3).type, 24 | templ.getField(3).length 25 | ) 26 | } 27 | /** Set Transmission Date Time **/ 28 | if (isoData.transmissionDateTime != null && !StringUtil.isEmpty(isoData.transmissionDateTime)) { 29 | message.setValue( 30 | 7, 31 | isoData.transmissionDateTime, 32 | templ.getField(7).type, 33 | templ.getField(7).length 34 | ) 35 | } 36 | /** Set STAN **/ 37 | if (isoData.systemTraceAuditNumber != null && !StringUtil.isEmpty(isoData.systemTraceAuditNumber)) { 38 | message.setValue( 39 | 11, 40 | isoData.systemTraceAuditNumber, 41 | templ.getField(11).type, 42 | templ.getField(11).length 43 | ) 44 | } 45 | /** Set Local Time **/ 46 | if (isoData.localTime != null && !StringUtil.isEmpty(isoData.localTime)) { 47 | message.setValue( 48 | 12, 49 | isoData.localTime, 50 | templ.getField(12).type, 51 | templ.getField(12).length 52 | ) 53 | } 54 | /** Set Local Date **/ 55 | if (isoData.localDate != null && !StringUtil.isEmpty(isoData.localDate)) { 56 | message.setValue( 57 | 13, 58 | isoData.localDate, 59 | templ.getField(13).type, 60 | templ.getField(13).length 61 | ) 62 | } 63 | /** Set Card Acceptor Terminal Code or Terminal ID **/ 64 | if (isoData.cardAcceptorTerminalId != null && !StringUtil.isEmpty(isoData.cardAcceptorTerminalId)) { 65 | message.setValue( 66 | 41, 67 | isoData.cardAcceptorTerminalId, 68 | templ.getField(41).type, 69 | templ.getField(41).length 70 | ) 71 | } 72 | /** Set Primary Message Hash **/ 73 | if (!StringUtil.isEmpty(terminalSessionKey)) { 74 | message.setValue( 75 | 64, 76 | String(byteArrayOf(0x0)), 77 | templ.getField(64).type, 78 | templ.getField(64).length 79 | ) 80 | val bytes = message.writeData() 81 | val length = bytes.size 82 | val temp = ByteArray(length - 64) 83 | if (length >= 64) { 84 | System.arraycopy(bytes, 0, temp, 0, length - 64) 85 | } 86 | val hashValue: String = KeyUtil().getMac(terminalSessionKey, temp) 87 | message.setValue( 88 | 64, 89 | hashValue, 90 | templ.getField(64).type, 91 | templ.getField(64).length 92 | ) 93 | } else if (isoData.primaryMessageHashValue != null && !StringUtil.isEmpty(isoData.primaryMessageHashValue)){ 94 | message.setValue( 95 | 64, 96 | isoData.primaryMessageHashValue, 97 | templ.getField(64).type, 98 | templ.getField(64).length 99 | ) 100 | } 101 | return ISOMessage(message) 102 | } 103 | 104 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/utils/TimeUtil.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.utils 2 | 3 | import com.subsidian.emvcardmanager.utils.TimeUtil 4 | import java.lang.Exception 5 | import java.text.ParseException 6 | import java.text.SimpleDateFormat 7 | import java.util.* 8 | 9 | class TimeUtil { 10 | 11 | val TIMEZONE_LAGOS = "Africa/Lagos" 12 | 13 | fun hasExpired(expDate: String): Boolean { 14 | val expYr = getYearFromExpDate(expDate) 15 | val expMon = getMonthFromExpDate(expDate) 16 | val expYrInt = expYr.toInt() 17 | val expMonInt = expMon.toInt() 18 | val currYrInt = Calendar.getInstance()[Calendar.YEAR] 19 | val currMonInt = Calendar.getInstance()[Calendar.MONTH] + 1 20 | if (expYrInt > currYrInt) return false 21 | if (expYrInt == currYrInt) { 22 | if (expMonInt > currMonInt) return false 23 | if (expMonInt == currMonInt) { 24 | val dayOfMonth = Calendar.getInstance()[Calendar.DAY_OF_MONTH] 25 | val lastDayOfCurrMonth = 26 | Calendar.getInstance().getActualMaximum(Calendar.DAY_OF_MONTH) 27 | if (dayOfMonth < lastDayOfCurrMonth) return false 28 | } 29 | } 30 | return true 31 | } 32 | 33 | fun getTimeInEpoch(date: Date?): Long { 34 | try { 35 | val sdfDate = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") 36 | sdfDate.timeZone = TimeZone.getTimeZone(TIMEZONE_LAGOS) 37 | val nowStr = sdfDate.format(date) 38 | val newDate = sdfDate.parse(nowStr) 39 | return newDate.time / 1000 40 | } catch (ex: Exception) { 41 | ex.printStackTrace() 42 | } 43 | return System.currentTimeMillis() 44 | } 45 | 46 | fun getEpoch(milli: Long): Long { 47 | val calendar = Calendar.getInstance() 48 | calendar.time = Date(milli) 49 | return calendar.time.time 50 | } 51 | 52 | fun getStartOfDayEpoch(epoch: Long): Long { 53 | try { 54 | val sdfDate = SimpleDateFormat("yyyy-MM-dd HH:mm:ss") 55 | sdfDate.timeZone = TimeZone.getTimeZone(TIMEZONE_LAGOS) 56 | val date = Date(epoch * 1000) 57 | val cal = Calendar.getInstance() 58 | cal.time = date 59 | val year = cal[Calendar.YEAR] 60 | val month = cal[Calendar.MONTH] + 1 61 | val day = cal[Calendar.DAY_OF_MONTH] 62 | val dateStr = "$year-$month-$day 00:00:00" 63 | val newDate = sdfDate.parse(dateStr) 64 | return newDate.time / 1000 65 | } catch (ex: Exception) { 66 | ex.printStackTrace() 67 | } 68 | return 0 69 | } 70 | 71 | fun getStartOfDay(date: Date?): Long { 72 | try { 73 | val c = Calendar.getInstance() 74 | c.time = date 75 | c[Calendar.HOUR_OF_DAY] = 0 76 | c[Calendar.MINUTE] = 0 77 | c[Calendar.SECOND] = 0 78 | c[Calendar.MILLISECOND] = 0 79 | return c.time.time 80 | } catch (ex: Exception) { 81 | ex.printStackTrace() 82 | } 83 | return 0 84 | } 85 | 86 | fun getEndOfDay(epoch: Long): Long { 87 | return getStartOfDayEpoch(epoch) + 24 * 60 * 60 88 | } 89 | 90 | fun getYearFromExpDate(expDate: String): String { 91 | val expYr = expDate.substring(0, 2) 92 | val currentYr = 93 | Calendar.getInstance()[Calendar.YEAR].toString() 94 | return currentYr.substring(0, 2) + expYr 95 | } 96 | 97 | fun getMonthFromExpDate(expDate: String): String { 98 | return expDate.substring(2) 99 | } 100 | 101 | fun getDateTimeMMddhhmmss(date: Date?): String { 102 | val sdfDate = SimpleDateFormat("MMddHHmmss") 103 | sdfDate.timeZone = TimeZone.getTimeZone(TIMEZONE_LAGOS) 104 | return sdfDate.format(date) 105 | } 106 | 107 | fun getDateTimeYyyyMMddhhmmss(date: Date?): String { 108 | val sdfDate = SimpleDateFormat("yyyyMMddHHmmss") 109 | sdfDate.timeZone = TimeZone.getTimeZone(TIMEZONE_LAGOS) 110 | return sdfDate.format(date) 111 | } 112 | 113 | fun getTimehhmmss(date: Date?): String { 114 | val sdfDate = SimpleDateFormat("HHmmss") 115 | sdfDate.timeZone = TimeZone.getTimeZone(TIMEZONE_LAGOS) 116 | return sdfDate.format(date) 117 | } 118 | 119 | fun getDateMMdd(date: Date?): String { 120 | val sdfDate = SimpleDateFormat("MMdd") 121 | sdfDate.timeZone = TimeZone.getTimeZone(TIMEZONE_LAGOS) 122 | return sdfDate.format(date) 123 | } 124 | 125 | fun getCustomDate(date: Date?, format: String?): String { 126 | val sdfDate = SimpleDateFormat(format, Locale.getDefault()) 127 | sdfDate.timeZone = TimeZone.getTimeZone(TIMEZONE_LAGOS) 128 | return sdfDate.format(date) 129 | } 130 | 131 | fun dateStringToDate(pattern: String?, dateString: String?): Date { 132 | return try { 133 | SimpleDateFormat(pattern).parse(dateString) 134 | } catch (e: ParseException) { 135 | Date() 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/transactions/AIDRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.builders.transactions 2 | 3 | import com.solab.iso8583.IsoMessage 4 | import com.solab.iso8583.MessageFactory 5 | import com.subsidian.emvcardmanager.entities.ISOData 6 | import com.subsidian.emvcardmanager.entities.ISOMessage 7 | import com.subsidian.emvcardmanager.enums.ISOMessageType 8 | import com.subsidian.emvcardmanager.utils.KeyUtil 9 | import com.subsidian.emvcardmanager.utils.StringUtil 10 | import java.lang.StringBuilder 11 | 12 | object AIDRequestBuilder { 13 | 14 | fun build (isoData: ISOData, messageFactory: MessageFactory, terminalSessionKey: String = ""): ISOMessage { 15 | val type: Int = ISOMessageType._0800.value.toInt(16) 16 | val message: IsoMessage = messageFactory.newMessage(type) 17 | val templ: IsoMessage = messageFactory.getMessageTemplate(type) 18 | val managementDataOne = StringBuilder().append("01") 19 | /** Set ProcCode **/ 20 | if (isoData.processingCode != null && !StringUtil.isEmpty(isoData.processingCode)) { 21 | message.setValue( 22 | 3, 23 | isoData.processingCode, 24 | templ.getField(3).type, 25 | templ.getField(3).length 26 | ) 27 | } 28 | /** Set Transmission Date Time **/ 29 | if (isoData.transmissionDateTime != null && !StringUtil.isEmpty(isoData.transmissionDateTime)) { 30 | message.setValue( 31 | 7, 32 | isoData.transmissionDateTime, 33 | templ.getField(7).type, 34 | templ.getField(7).length 35 | ) 36 | } 37 | /** Set STAN **/ 38 | if (isoData.systemTraceAuditNumber != null && !StringUtil.isEmpty(isoData.systemTraceAuditNumber)) { 39 | message.setValue( 40 | 11, 41 | isoData.systemTraceAuditNumber, 42 | templ.getField(11).type, 43 | templ.getField(11).length 44 | ) 45 | } 46 | /** Set Local Time **/ 47 | if (isoData.localTime != null && !StringUtil.isEmpty(isoData.localTime)) { 48 | message.setValue( 49 | 12, 50 | isoData.localTime, 51 | templ.getField(12).type, 52 | templ.getField(12).length 53 | ) 54 | } 55 | /** Set Local Date **/ 56 | if (isoData.localDate != null && !StringUtil.isEmpty(isoData.localDate)) { 57 | message.setValue( 58 | 13, 59 | isoData.localDate, 60 | templ.getField(13).type, 61 | templ.getField(13).length 62 | ) 63 | } 64 | /** Set Card Acceptor Terminal Code or Terminal ID **/ 65 | if (isoData.cardAcceptorTerminalId != null && !StringUtil.isEmpty(isoData.cardAcceptorTerminalId)) { 66 | val cardAcceptorTerminalIdLength: String = StringUtil.leftPadding('0', 3, isoData.cardAcceptorTerminalId!!.length.toString()) 67 | managementDataOne 68 | .append(cardAcceptorTerminalIdLength) 69 | .append(isoData.cardAcceptorTerminalId) 70 | message.setValue( 71 | 41, 72 | isoData.cardAcceptorTerminalId, 73 | templ.getField(41).type, 74 | templ.getField(41).length 75 | ) 76 | } 77 | /** Set Management Data One **/ 78 | if (isoData.managementDataOnePrivate != null && !StringUtil.isEmpty(isoData.managementDataOnePrivate)) { 79 | message.setValue( 80 | 62, 81 | isoData.managementDataOnePrivate, 82 | templ.getField(62).type, 83 | isoData.managementDataOnePrivate!!.length) 84 | } else { 85 | message.setValue( 86 | 62, 87 | managementDataOne.toString(), 88 | templ.getField(62).type, 89 | managementDataOne.toString().length) 90 | } 91 | /** Set Primary Message Hash **/ 92 | if (!StringUtil.isEmpty(terminalSessionKey)) { 93 | message.setValue( 94 | 64, 95 | String(byteArrayOf(0x0)), 96 | templ.getField(64).type, 97 | templ.getField(64).length 98 | ) 99 | val bytes = message.writeData() 100 | val length = bytes.size 101 | val temp = ByteArray(length - 64) 102 | if (length >= 64) { 103 | System.arraycopy(bytes, 0, temp, 0, length - 64) 104 | } 105 | val hashValue: String = KeyUtil().getMac(terminalSessionKey, temp) 106 | message.setValue( 107 | 64, 108 | hashValue, 109 | templ.getField(64).type, 110 | templ.getField(64).length 111 | ) 112 | } else if (isoData.primaryMessageHashValue != null && !StringUtil.isEmpty(isoData.primaryMessageHashValue)){ 113 | message.setValue( 114 | 64, 115 | isoData.primaryMessageHashValue, 116 | templ.getField(64).type, 117 | templ.getField(64).length 118 | ) 119 | } 120 | return ISOMessage(message) 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/transactions/CAPKRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.builders.transactions 2 | 3 | import com.solab.iso8583.IsoMessage 4 | import com.solab.iso8583.MessageFactory 5 | import com.subsidian.emvcardmanager.entities.ISOData 6 | import com.subsidian.emvcardmanager.entities.ISOMessage 7 | import com.subsidian.emvcardmanager.enums.ISOMessageType 8 | import com.subsidian.emvcardmanager.utils.KeyUtil 9 | import com.subsidian.emvcardmanager.utils.StringUtil 10 | import java.lang.StringBuilder 11 | 12 | object CAPKRequestBuilder { 13 | 14 | fun build (isoData: ISOData, messageFactory: MessageFactory, terminalSessionKey: String = ""): ISOMessage { 15 | val type: Int = ISOMessageType._0800.value.toInt(16) 16 | val message: IsoMessage = messageFactory.newMessage(type) 17 | val templ: IsoMessage = messageFactory.getMessageTemplate(type) 18 | val managementDataOne = StringBuilder().append("01") 19 | /** Set ProcCode **/ 20 | if (isoData.processingCode != null && !StringUtil.isEmpty(isoData.processingCode)) { 21 | message.setValue( 22 | 3, 23 | isoData.processingCode, 24 | templ.getField(3).type, 25 | templ.getField(3).length 26 | ) 27 | } 28 | /** Set Transmission Date Time **/ 29 | if (isoData.transmissionDateTime != null && !StringUtil.isEmpty(isoData.transmissionDateTime)) { 30 | message.setValue( 31 | 7, 32 | isoData.transmissionDateTime, 33 | templ.getField(7).type, 34 | templ.getField(7).length 35 | ) 36 | } 37 | /** Set STAN **/ 38 | if (isoData.systemTraceAuditNumber != null && !StringUtil.isEmpty(isoData.systemTraceAuditNumber)) { 39 | message.setValue( 40 | 11, 41 | isoData.systemTraceAuditNumber, 42 | templ.getField(11).type, 43 | templ.getField(11).length 44 | ) 45 | } 46 | /** Set Local Time **/ 47 | if (isoData.localTime != null && !StringUtil.isEmpty(isoData.localTime)) { 48 | message.setValue( 49 | 12, 50 | isoData.localTime, 51 | templ.getField(12).type, 52 | templ.getField(12).length 53 | ) 54 | } 55 | /** Set Local Date **/ 56 | if (isoData.localDate != null && !StringUtil.isEmpty(isoData.localDate)) { 57 | message.setValue( 58 | 13, 59 | isoData.localDate, 60 | templ.getField(13).type, 61 | templ.getField(13).length 62 | ) 63 | } 64 | /** Set Card Acceptor Terminal Code or Terminal ID **/ 65 | if (isoData.cardAcceptorTerminalId != null && !StringUtil.isEmpty(isoData.cardAcceptorTerminalId)) { 66 | val cardAcceptorTerminalIdLength: String = StringUtil.leftPadding('0', 3, isoData.cardAcceptorTerminalId!!.length.toString()) 67 | managementDataOne 68 | .append(cardAcceptorTerminalIdLength) 69 | .append(isoData.cardAcceptorTerminalId) 70 | message.setValue( 71 | 41, 72 | isoData.cardAcceptorTerminalId, 73 | templ.getField(41).type, 74 | templ.getField(41).length 75 | ) 76 | } 77 | /** Set Management Data One **/ 78 | if (isoData.managementDataOnePrivate != null && !StringUtil.isEmpty(isoData.managementDataOnePrivate)) { 79 | message.setValue( 80 | 62, 81 | isoData.managementDataOnePrivate, 82 | templ.getField(62).type, 83 | isoData.managementDataOnePrivate!!.length) 84 | } else { 85 | message.setValue( 86 | 62, 87 | managementDataOne.toString(), 88 | templ.getField(62).type, 89 | managementDataOne.toString().length) 90 | } 91 | /** Set Primary Message Hash **/ 92 | if (!StringUtil.isEmpty(terminalSessionKey)) { 93 | message.setValue( 94 | 64, 95 | String(byteArrayOf(0x0)), 96 | templ.getField(64).type, 97 | templ.getField(64).length 98 | ) 99 | val bytes = message.writeData() 100 | val length = bytes.size 101 | val temp = ByteArray(length - 64) 102 | if (length >= 64) { 103 | System.arraycopy(bytes, 0, temp, 0, length - 64) 104 | } 105 | val hashValue: String = KeyUtil().getMac(terminalSessionKey, temp) 106 | message.setValue( 107 | 64, 108 | hashValue, 109 | templ.getField(64).type, 110 | templ.getField(64).length 111 | ) 112 | } else if (isoData.primaryMessageHashValue != null && !StringUtil.isEmpty(isoData.primaryMessageHashValue)){ 113 | message.setValue( 114 | 64, 115 | isoData.primaryMessageHashValue, 116 | templ.getField(64).type, 117 | templ.getField(64).length 118 | ) 119 | } 120 | return ISOMessage(message) 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/transactions/DailyReportRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.builders.transactions 2 | 3 | import com.solab.iso8583.IsoMessage 4 | import com.solab.iso8583.MessageFactory 5 | import com.subsidian.emvcardmanager.entities.ISOData 6 | import com.subsidian.emvcardmanager.entities.ISOMessage 7 | import com.subsidian.emvcardmanager.enums.ISOMessageType 8 | import com.subsidian.emvcardmanager.utils.KeyUtil 9 | import com.subsidian.emvcardmanager.utils.StringUtil 10 | import java.lang.StringBuilder 11 | 12 | object DailyReportRequestBuilder { 13 | 14 | fun build (isoData: ISOData, messageFactory: MessageFactory, terminalSessionKey: String = ""): ISOMessage { 15 | val type: Int = ISOMessageType._0800.value.toInt(16) 16 | val message: IsoMessage = messageFactory.newMessage(type) 17 | val templ: IsoMessage = messageFactory.getMessageTemplate(type) 18 | val managementDataTwo = StringBuilder().append("01") 19 | /** Set ProcCode **/ 20 | if (isoData.processingCode != null && !StringUtil.isEmpty(isoData.processingCode)) { 21 | message.setValue( 22 | 3, 23 | isoData.processingCode, 24 | templ.getField(3).type, 25 | templ.getField(3).length 26 | ) 27 | } 28 | /** Set Transmission Date Time **/ 29 | if (isoData.transmissionDateTime != null && !StringUtil.isEmpty(isoData.transmissionDateTime)) { 30 | message.setValue( 31 | 7, 32 | isoData.transmissionDateTime, 33 | templ.getField(7).type, 34 | templ.getField(7).length 35 | ) 36 | } 37 | /** Set STAN **/ 38 | if (isoData.systemTraceAuditNumber != null && !StringUtil.isEmpty(isoData.systemTraceAuditNumber)) { 39 | message.setValue( 40 | 11, 41 | isoData.systemTraceAuditNumber, 42 | templ.getField(11).type, 43 | templ.getField(11).length 44 | ) 45 | } 46 | /** Set Local Time **/ 47 | if (isoData.localTime != null && !StringUtil.isEmpty(isoData.localTime)) { 48 | message.setValue( 49 | 12, 50 | isoData.localTime, 51 | templ.getField(12).type, 52 | templ.getField(12).length 53 | ) 54 | } 55 | /** Set Local Date **/ 56 | if (isoData.localDate != null && !StringUtil.isEmpty(isoData.localDate)) { 57 | message.setValue( 58 | 13, 59 | isoData.localDate, 60 | templ.getField(13).type, 61 | templ.getField(13).length 62 | ) 63 | } 64 | /** Set Card Acceptor Terminal Code or Terminal ID **/ 65 | if (isoData.cardAcceptorTerminalId != null && !StringUtil.isEmpty(isoData.cardAcceptorTerminalId)) { 66 | val cardAcceptorTerminalIdLength: String = StringUtil.leftPadding('0', 3, isoData.cardAcceptorTerminalId!!.length.toString()) 67 | managementDataTwo 68 | .append(cardAcceptorTerminalIdLength) 69 | .append(isoData.cardAcceptorTerminalId) 70 | message.setValue( 71 | 41, 72 | isoData.cardAcceptorTerminalId, 73 | templ.getField(41).type, 74 | templ.getField(41).length 75 | ) 76 | } 77 | /** Set Management Data Two **/ 78 | if (isoData.managementDataTwoPrivate != null && !StringUtil.isEmpty(isoData.managementDataTwoPrivate)) { 79 | message.setValue( 80 | 63, 81 | isoData.managementDataTwoPrivate, 82 | templ.getField(63).type, 83 | isoData.managementDataTwoPrivate!!.length) 84 | } else { 85 | message.setValue( 86 | 63, 87 | managementDataTwo.toString(), 88 | templ.getField(63).type, 89 | managementDataTwo.toString().length) 90 | } 91 | /** Set Primary Message Hash **/ 92 | if (!StringUtil.isEmpty(terminalSessionKey)) { 93 | message.setValue( 94 | 64, 95 | String(byteArrayOf(0x0)), 96 | templ.getField(64).type, 97 | templ.getField(64).length 98 | ) 99 | val bytes = message.writeData() 100 | val length = bytes.size 101 | val temp = ByteArray(length - 64) 102 | if (length >= 64) { 103 | System.arraycopy(bytes, 0, temp, 0, length - 64) 104 | } 105 | val hashValue: String = KeyUtil().getMac(terminalSessionKey, temp) 106 | message.setValue( 107 | 64, 108 | hashValue, 109 | templ.getField(64).type, 110 | templ.getField(64).length 111 | ) 112 | } else if (isoData.primaryMessageHashValue != null && !StringUtil.isEmpty(isoData.primaryMessageHashValue)){ 113 | message.setValue( 114 | 64, 115 | isoData.primaryMessageHashValue, 116 | templ.getField(64).type, 117 | templ.getField(64).length 118 | ) 119 | } 120 | return ISOMessage(message) 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/transactions/TerminalParameterRequestBuilder.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.builders.transactions 2 | 3 | import com.solab.iso8583.IsoMessage 4 | import com.solab.iso8583.MessageFactory 5 | import com.subsidian.emvcardmanager.entities.ISOData 6 | import com.subsidian.emvcardmanager.entities.ISOMessage 7 | import com.subsidian.emvcardmanager.enums.ISOMessageType 8 | import com.subsidian.emvcardmanager.utils.KeyUtil 9 | import com.subsidian.emvcardmanager.utils.StringUtil 10 | import java.lang.StringBuilder 11 | 12 | object TerminalParameterRequestBuilder { 13 | 14 | fun build (isoData: ISOData, messageFactory: MessageFactory, terminalSessionKey: String = ""): ISOMessage { 15 | val type: Int = ISOMessageType._0800.value.toInt(16) 16 | val message: IsoMessage = messageFactory.newMessage(type) 17 | val templ: IsoMessage = messageFactory.getMessageTemplate(type) 18 | val managementDataOne = StringBuilder().append("01") 19 | /** Set ProcCode **/ 20 | if (isoData.processingCode != null && !StringUtil.isEmpty(isoData.processingCode)) { 21 | message.setValue( 22 | 3, 23 | isoData.processingCode, 24 | templ.getField(3).type, 25 | templ.getField(3).length 26 | ) 27 | } 28 | /** Set Transmission Date Time **/ 29 | if (isoData.transmissionDateTime != null && !StringUtil.isEmpty(isoData.transmissionDateTime)) { 30 | message.setValue( 31 | 7, 32 | isoData.transmissionDateTime, 33 | templ.getField(7).type, 34 | templ.getField(7).length 35 | ) 36 | } 37 | /** Set STAN **/ 38 | if (isoData.systemTraceAuditNumber != null && !StringUtil.isEmpty(isoData.systemTraceAuditNumber)) { 39 | message.setValue( 40 | 11, 41 | isoData.systemTraceAuditNumber, 42 | templ.getField(11).type, 43 | templ.getField(11).length 44 | ) 45 | } 46 | /** Set Local Time **/ 47 | if (isoData.localTime != null && !StringUtil.isEmpty(isoData.localTime)) { 48 | message.setValue( 49 | 12, 50 | isoData.localTime, 51 | templ.getField(12).type, 52 | templ.getField(12).length 53 | ) 54 | } 55 | /** Set Local Date **/ 56 | if (isoData.localDate != null && !StringUtil.isEmpty(isoData.localDate)) { 57 | message.setValue( 58 | 13, 59 | isoData.localDate, 60 | templ.getField(13).type, 61 | templ.getField(13).length 62 | ) 63 | } 64 | /** Set Card Acceptor Terminal Code or Terminal ID **/ 65 | if (isoData.cardAcceptorTerminalId != null && !StringUtil.isEmpty(isoData.cardAcceptorTerminalId)) { 66 | val cardAcceptorTerminalIdLength: String = StringUtil.leftPadding('0', 3, isoData.cardAcceptorTerminalId!!.length.toString()) 67 | managementDataOne 68 | .append(cardAcceptorTerminalIdLength) 69 | .append(isoData.cardAcceptorTerminalId) 70 | message.setValue( 71 | 41, 72 | isoData.cardAcceptorTerminalId, 73 | templ.getField(41).type, 74 | templ.getField(41).length 75 | ) 76 | } 77 | /** Set Management Data One **/ 78 | if (isoData.managementDataOnePrivate != null && !StringUtil.isEmpty(isoData.managementDataOnePrivate)) { 79 | message.setValue( 80 | 62, 81 | isoData.managementDataOnePrivate, 82 | templ.getField(62).type, 83 | isoData.managementDataOnePrivate!!.length) 84 | } else { 85 | message.setValue( 86 | 62, 87 | managementDataOne.toString(), 88 | templ.getField(62).type, 89 | managementDataOne.toString().length) 90 | } 91 | /** Set Primary Message Hash **/ 92 | if (!StringUtil.isEmpty(terminalSessionKey)) { 93 | message.setValue( 94 | 64, 95 | String(byteArrayOf(0x0)), 96 | templ.getField(64).type, 97 | templ.getField(64).length 98 | ) 99 | val bytes = message.writeData() 100 | val length = bytes.size 101 | val temp = ByteArray(length - 64) 102 | if (length >= 64) { 103 | System.arraycopy(bytes, 0, temp, 0, length - 64) 104 | } 105 | val hashValue: String = KeyUtil().getMac(terminalSessionKey, temp) 106 | message.setValue( 107 | 64, 108 | hashValue, 109 | templ.getField(64).type, 110 | templ.getField(64).length 111 | ) 112 | } else if (isoData.primaryMessageHashValue != null && !StringUtil.isEmpty(isoData.primaryMessageHashValue)){ 113 | message.setValue( 114 | 64, 115 | isoData.primaryMessageHashValue, 116 | templ.getField(64).type, 117 | templ.getField(64).length 118 | ) 119 | } 120 | return ISOMessage(message) 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/entities/ISOData.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.entities 2 | 3 | data class ISOData( 4 | var messageType: String? = null, 5 | var primaryAccountNumber: String? = null, // 2 Primary account number Mandatory 6 | var processingCode: String? = null, // 3 Processing code Mandatory 7 | var transactionAmount: String? = null, // 4 Amount, transaction Mandatory 8 | var transmissionDateTime: String? = null, // 7 Transmission date and time Conditional 9 | var settlementConversionRate: String? = null, // 9 Conversion rate, settlement Conditional 10 | var systemTraceAuditNumber: String? = null, // 11 Systems trace audit number Conditional 11 | var localTime: String? = null, // 12 Time, local transaction Mandatory 12 | var localDate: String? = null, // 13 Date, local transaction Conditional 13 | var expirationDate: String? = null, // 14 Date, expiration Conditional 14 | var settlementDate: String? = null, // 15 Date, Settlement Conditional 15 | var conversionDate: String? = null, // 16 Date, conversion Conditional 16 | var merchantType: String? = null, // 18 Merchant’s type Mandatory 17 | var posEntryMode: String? = null, // 22 POS entry mode Mandatory 18 | var cardSequenceNumber: String? = null, // 23 Card sequence number Conditional 19 | var posConditionCode: String? = null, // 25 POS condition code Mandatory 20 | var posPinCaptureCode: String? = null, // 26 POS PIN capture code Conditional 21 | var transactionFeeAmount: String? = null, // 28 Amount, transaction fee Conditional 22 | var settlementAmount: String? = null, // 29 Amount, settlement fee Conditional 23 | var transactionProcessingFeeAmount: String? = null, // 30 Amount, transaction processing fee Conditional 24 | var settleProcessingFeeAmount: String? = null, // 31 Amount, settle processing fee Conditional 25 | var acquiringInstitutionId: String? = null, // 32 Acquiring institution id code Mandatory 26 | var forwardingInstitutionId: String? = null, // 33 Forwarding institution id code Conditional 27 | var trackTwoData: String? = null, // 35 Track 2 data Conditional 28 | var retrievalReferenceNumber: String? = null, // 37 Retrieval reference number Mandatory 29 | var authorizationIdResponse: String? = null, // 38 Authorization id response Conditional 30 | var responseCode: String? = null, // 39 Response code Mandatory 31 | var serviceRestrictionCode: String? = null, // 40 Service restriction code Conditional 32 | var cardAcceptorTerminalId: String? = null, // 41 Card acceptor terminal id Optional 33 | var cardAcceptorIdCode: String? = null, // 42 Card acceptor id code Conditional 34 | var cardAcceptorNameLocation: String? = null, // 43 Card acceptor name/location Conditional 35 | var additionalResponseData: String? = null, // 44 Additional response data Optional 36 | var additionalData: String? = null, // 48 Additional data Conditional 37 | var transactionCurrencyCode: String? = null, // 49 Currency code, transaction Mandatory 38 | var settlementCurrencyCode: String? = null, // 50 Currency code, settlement Conditional 39 | var pinData: String? = null, // 52 PIN data Conditional 40 | var securityRelatedControlInformation: String? = null, // 53 Security related control information Conditional 41 | var additionalAmounts: String? = null, // 54 Additional amounts Conditional 42 | var integratedCircuitCardData: String? = null, // 55 Integrated Circuit Card System Related Data Conditional 43 | var messageReasonCode: String? = null, // 56 Message reason code Optional 44 | var authorizingAgentId: String? = null, // 58 Authorizing agent id code Conditional 45 | var transportEchoData: String? = null, // 59 Transport (echo) data Conditional 46 | var paymentInformation: String? = null, // 60 Payment Information Conditional 47 | var managementDataOnePrivate: String? = null, // 62 Private, management data 1 Conditional 48 | var managementDataTwoPrivate: String? = null, // 63 Private, management data 2 Conditional 49 | var primaryMessageHashValue: String? = null, // 64 Primary Message Hash Value Conditional 50 | var extendedPaymentCode: String? = null, // 67 Extended payment code Conditional 51 | var originalDataElement: String? = null, // 90 Original data elements Mandatory 52 | var replacementAmount: String? = null, // 95 Replacement Amounts Mandatory 53 | var payee: String? = null, // 98 Payee Conditional 54 | var receivingInstitutionId: String? = null, // 100 Receiving Institution ID Code Optional 55 | var accountIdentification1: String? = null, // 102 Account identification 1 Optional 56 | var accountIdentification2: String? = null, // 103 Account identification 2 Optional 57 | var posDataCode: String? = null, // 123 POS data code Mandatory 58 | var nearFieldCommunicationData: String? = null, // 124 Near Field Communication Data Conditional 59 | var secondaryMessageHashValue: String? = null, // 128 Secondary Message Hash Value Mandatory 60 | ) 61 | -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/handlers/IOSocketHandler.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.handlers 2 | 3 | import android.util.Log 4 | import com.subsidian.emvcardmanager.exceptions.ISOClientException 5 | import com.subsidian.emvcardmanager.interfaces.ISOClientEventListener 6 | import com.subsidian.emvcardmanager.interfaces.SocketHandler 7 | import kotlinx.coroutines.Dispatchers 8 | import kotlinx.coroutines.GlobalScope 9 | import kotlinx.coroutines.launch 10 | import java.io.BufferedInputStream 11 | import java.io.BufferedOutputStream 12 | import java.io.IOException 13 | import java.net.Socket 14 | import java.net.SocketException 15 | import java.net.SocketTimeoutException 16 | import java.nio.BufferOverflowException 17 | import java.nio.ByteBuffer 18 | import java.util.* 19 | import javax.net.ssl.SSLContext 20 | import javax.net.ssl.SSLSocket 21 | import kotlin.experimental.and 22 | 23 | class IOSocketHandler : SocketHandler { 24 | 25 | private var socket: Socket? = null 26 | private var socketWriter: BufferedOutputStream? = null 27 | private var socketReader: BufferedInputStream? = null 28 | private var isoClientEventListener: ISOClientEventListener? = null 29 | 30 | @Throws(ISOClientException::class) 31 | override fun init( 32 | host: String, 33 | port: Int, 34 | isoClientEventListener: ISOClientEventListener?, 35 | sslHandler: SSLHandler? 36 | ) { 37 | this.isoClientEventListener = isoClientEventListener 38 | var context: SSLContext? = null 39 | try { 40 | context = sslHandler?.context 41 | val sslsocketfactory = context?.socketFactory 42 | val socket = sslsocketfactory?.createSocket( 43 | host, port 44 | ) as SSLSocket 45 | socket.needClientAuth = false 46 | socket.startHandshake() 47 | this@IOSocketHandler.socket = socket 48 | postInit() 49 | } catch (e: Exception) { 50 | throw ISOClientException(e) 51 | } 52 | } 53 | 54 | @Throws(IOException::class) 55 | override fun init(host: String, port: Int, isoClientEventListener: ISOClientEventListener?) { 56 | this.isoClientEventListener = isoClientEventListener 57 | this@IOSocketHandler.socket = Socket(host, port) 58 | postInit() 59 | } 60 | 61 | @Throws(IOException::class) 62 | private fun postInit() { 63 | this@IOSocketHandler.socketWriter = BufferedOutputStream(socket!!.getOutputStream()) 64 | } 65 | 66 | @Throws(IOException::class, ISOClientException::class) 67 | override fun sendMessageSync(buffer: ByteBuffer, length: Int): ByteArray { 68 | val bufferCapacity = 1024 69 | isoClientEventListener!!.beforeSendingMessage() 70 | for (v in buffer.array()) { 71 | socketWriter!!.write(v.toInt()) 72 | } 73 | socketWriter!!.flush() 74 | socketReader = BufferedInputStream(socket!!.getInputStream()) 75 | isoClientEventListener!!.afterSendingMessage() 76 | isoClientEventListener!!.beforeReceiveResponse() 77 | var readBuffer = ByteBuffer.allocate(bufferCapacity) 78 | return try { 79 | if (length > 0) { 80 | val bLen = ByteArray(length) 81 | socketReader!!.read(bLen, 0, length) 82 | val mLen: Int = (bLen[0] and (0xff).toByte()) + (bLen[1] and (0xff).toByte()) 83 | } 84 | var r: Int 85 | var fo = 512 86 | /** To handle buffer overflow **/ 87 | if (socketReader!!.available() > bufferCapacity) { 88 | readBuffer.clear() 89 | readBuffer.compact() 90 | readBuffer = ByteBuffer.allocate(socketReader!!.available()) 91 | } else if(socketReader!!.available() == 0) { 92 | readBuffer.clear() 93 | readBuffer.compact() 94 | readBuffer = ByteBuffer.allocate(bufferCapacity * 10) 95 | } 96 | do { 97 | r = socketReader!!.read() 98 | if (!(r == -1 && socketReader!!.available() == 0) && readBuffer.remaining() > 0) { 99 | readBuffer.put(r.toByte()) 100 | } else { 101 | fo-- 102 | } 103 | } while ((r > -1 && socketReader!!.available() > 0 || 104 | r == -1 && readBuffer.position() <= 1) && fo > 0) 105 | val resp = Arrays.copyOfRange(readBuffer.array(), 0, readBuffer.position()) 106 | isoClientEventListener!!.afterReceiveResponse() 107 | resp 108 | } 109 | catch (e: SocketTimeoutException) { 110 | if (isoClientEventListener != null) { 111 | isoClientEventListener!!.connectionTimeout() 112 | } 113 | throw ISOClientException("Read Timeout") 114 | } 115 | finally { 116 | readBuffer.clear() 117 | readBuffer.compact() 118 | } 119 | } 120 | 121 | @Synchronized 122 | override fun close() { 123 | try { 124 | if (socketWriter != null) socketWriter!!.close() 125 | if (socketReader != null) socketReader!!.close() 126 | if (socket != null) socket!!.close() 127 | } catch (e: IOException) { 128 | e.printStackTrace() 129 | } 130 | } 131 | 132 | @Throws(SocketException::class) 133 | override fun setReadTimeout(readTimeout: Int) { 134 | socket?.soTimeout = readTimeout 135 | } 136 | 137 | override fun isConnected(): Boolean { 138 | return if (socket != null) socket!!.isConnected else false 139 | } 140 | 141 | override fun isClosed(): Boolean { 142 | return socket == null || socket!!.isClosed 143 | } 144 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /emvcardmanager/src/main/java/com/subsidian/emvcardmanager/utils/StringUtil.kt: -------------------------------------------------------------------------------- 1 | package com.subsidian.emvcardmanager.utils 2 | 3 | import java.lang.StringBuilder 4 | import java.nio.ByteBuffer 5 | import java.util.* 6 | import kotlin.experimental.and 7 | 8 | object StringUtil { 9 | 10 | private val hexArray = "0123456789ABCDEF".toCharArray() 11 | 12 | fun fromByteArray(data: ByteArray): String { 13 | val hexChars = CharArray(data.size * 2) 14 | for (j in data.indices) { 15 | val v: Int = (data[j] and (0xFF).toByte()).toInt() 16 | hexChars[j * 2] = hexArray[v ushr 4] 17 | hexChars[j * 2 + 1] = hexArray[v and 0x0F] 18 | } 19 | return String(hexChars) 20 | } 21 | 22 | fun asciiFromByteArray(data: ByteArray): String { 23 | return hexToAscii(fromByteArray(data)) 24 | } 25 | 26 | //it's come from http://www.baeldung.com/java-convert-hex-to-ascii 27 | fun asciiToHex(asciiStr: String): String { 28 | val chars = asciiStr.toCharArray() 29 | val hex = StringBuilder() 30 | for (ch in chars) { 31 | hex.append(Integer.toHexString(ch.toInt())) 32 | } 33 | return hex.toString() 34 | } 35 | 36 | //it's come from http://www.baeldung.com/java-convert-hex-to-ascii 37 | fun hexToAscii(hexStr: String): String { 38 | val output = StringBuilder("") 39 | var i = 0 40 | while (i < hexStr.length) { 41 | val str = hexStr.substring(i, i + 2) 42 | output.append(str.toInt(16).toChar()) 43 | i += 2 44 | } 45 | return output.toString() 46 | } 47 | 48 | fun asciiToHex(data: ByteArray): ByteArray { 49 | var hexChars: CharArray? = CharArray(data.size * 2) 50 | for (j in data.indices) { 51 | val v: Int = (data[j] and (0xFF).toByte()).toInt() 52 | hexChars!![j * 2] = hexArray[v ushr 4] 53 | hexChars[j * 2 + 1] = hexArray[v and 0x0F] 54 | } 55 | val res = ByteArray(hexChars!!.size) 56 | for (i in hexChars.indices) { 57 | res[i] = hexChars[i].toByte() 58 | } 59 | Arrays.fill(hexChars, '\u0000') 60 | hexChars = null 61 | return res 62 | } 63 | 64 | fun hexStringToByteArray(s: String): ByteArray { 65 | var s = s 66 | var len = s.length 67 | var padd = false 68 | if (len % 2 != 0) { 69 | s = "0$s" 70 | len++ 71 | padd = true 72 | } 73 | val data = ByteArray(len / 2) 74 | var i = 0 75 | while (i < len) { 76 | data[i / 2] = ((Character.digit(s[i], 16) shl 4) 77 | + Character.digit(s[i + 1], 16)).toByte() 78 | i += 2 79 | } 80 | return data 81 | } 82 | 83 | fun fromByteBuffer(readBuffer: ByteBuffer): String { 84 | return fromByteArray(Arrays.copyOfRange(readBuffer.array(), 0, readBuffer.position())) 85 | } 86 | 87 | fun intToHexString(value: Int): String { 88 | var hs = Integer.toHexString(value) 89 | if (hs.length % 2 != 0) hs = "0$hs" 90 | hs = hs.toUpperCase() 91 | return hs 92 | } 93 | 94 | fun asciiToByteArray(bytes: ByteArray): ByteArray { 95 | return hexStringToByteArray(hexToAscii(fromByteArray(bytes))) 96 | } 97 | 98 | fun toHexString(str: String): String { 99 | val sb = StringBuffer() 100 | for (i in 0 until str.length) { 101 | sb.append(toHexString(str[i])) 102 | } 103 | return sb.toString() 104 | } 105 | 106 | /** 107 | * convert into Hexadecimal notation of Unicode.

108 | * example)a?\u0061 109 | * @param ch 110 | * @return 111 | */ 112 | fun toHexString(ch: Char): String { 113 | var hex = Integer.toHexString(ch.toInt()) 114 | while (hex.length < 4) { 115 | hex = "0$hex" 116 | } 117 | hex = "\\u$hex" 118 | return hex 119 | } 120 | 121 | /** 122 | * str fill,totalLength。 123 | * 124 | * @param fill 125 | * @param totalLength 126 | * @param str 127 | * @return 128 | */ 129 | fun leftPadding(fill: Char, totalLength: Int, str: String): String { 130 | val buffer = StringBuffer() 131 | for (i in str.length until totalLength) { 132 | buffer.append(fill) 133 | } 134 | buffer.append(str) 135 | return buffer.toString() 136 | } 137 | 138 | fun leftPadding(fill: String?, totalLength: Int, str: String): String { 139 | val buffer = StringBuffer() 140 | for (i in str.length until totalLength) { 141 | buffer.append(fill) 142 | } 143 | buffer.append(str) 144 | return buffer.toString() 145 | } 146 | 147 | fun leftAppend(fill: String?, appendLength: Int, str: String?): String { 148 | val buffer = StringBuffer() 149 | for (i in 0 until appendLength) { 150 | buffer.append(fill) 151 | } 152 | buffer.append(str) 153 | return buffer.toString() 154 | } 155 | 156 | fun leftPad(pad: Char = '0', len: Int, str: String): String { 157 | if (str == null) return "" 158 | val sb = StringBuilder() 159 | while (sb.length + str.length < len) { 160 | sb.append(pad) 161 | } 162 | sb.append(str) 163 | return sb.toString() 164 | } 165 | 166 | fun rightAppend(fill: String?, appendLength: Int, str: String?): String { 167 | val buffer = StringBuilder(str) 168 | for (i in 0 until appendLength) { 169 | buffer.append(fill) 170 | } 171 | return buffer.toString() 172 | } 173 | 174 | fun rightPadding(fill: String?, totalLength: Int, str: String): String { 175 | val buffer = StringBuilder(str) 176 | while (str.length < totalLength) { 177 | buffer.append(fill) 178 | } 179 | return buffer.toString() 180 | } 181 | 182 | fun rightPad(pad: Char, len: Int, str: String): String { 183 | if (str == null) return "" 184 | val sb = StringBuilder() 185 | sb.append(str) 186 | while (sb.length < len) { 187 | sb.append(pad) 188 | } 189 | return sb.toString() 190 | } 191 | 192 | fun isEmpty(msg: String?): Boolean { 193 | return !(msg != null && "" != msg) 194 | } 195 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/keyboard.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 14 | 15 | 35 | 36 | 37 | 38 | 39 | 47 | 48 | 49 | 50 | 55 | 56 | 61 | 62 | 67 | 68 | 69 | 70 | 71 | 76 | 77 | 82 | 83 | 88 | 89 | 90 | 91 | 92 | 97 | 98 | 103 | 104 | 109 | 110 | 111 | 112 | 113 | 119 | 120 | 125 | 126 | 133 | 140 | 141 | 143 |