├── 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 |
155 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/transactions/CallHomeRequestBuilder.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 CallHomeRequestBuilder {
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 | val appVersion = "001"
20 | val appVersionLen: String = StringUtil.leftPadding('0', 3, appVersion.length.toString())
21 | val paymentChannelMode = "00000000000000000000"
22 | val paymentChannelModeLen: String = StringUtil.leftPadding('0', 3, paymentChannelMode.length.toString())
23 | val callHomeComments = "N/A" // Call – Home Merchant Information/Complaint/Comments
24 | val callHomeCommentsLen: String = StringUtil.leftPadding('0', 3, callHomeComments.length.toString())
25 | val commsServiceProvider = "WIFI/MTN/AIRTEL/GLO" // Communications Service Provider
26 | val commsServiceProviderLen: String = StringUtil.leftPadding('0', 3, commsServiceProvider.length.toString())
27 | /** Set ProcCode **/
28 | if (isoData.processingCode != null && !StringUtil.isEmpty(isoData.processingCode)) {
29 | message.setValue(
30 | 3,
31 | isoData.processingCode,
32 | templ.getField(3).type,
33 | templ.getField(3).length
34 | )
35 | }
36 | /** Set Transmission Date Time **/
37 | if (isoData.transmissionDateTime != null && !StringUtil.isEmpty(isoData.transmissionDateTime)) {
38 | message.setValue(
39 | 7,
40 | isoData.transmissionDateTime,
41 | templ.getField(7).type,
42 | templ.getField(7).length
43 | )
44 | }
45 | /** Set STAN **/
46 | if (isoData.systemTraceAuditNumber != null && !StringUtil.isEmpty(isoData.systemTraceAuditNumber)) {
47 | message.setValue(
48 | 11,
49 | isoData.systemTraceAuditNumber,
50 | templ.getField(11).type,
51 | templ.getField(11).length
52 | )
53 | }
54 | /** Set Local Time **/
55 | if (isoData.localTime != null && !StringUtil.isEmpty(isoData.localTime)) {
56 | message.setValue(
57 | 12,
58 | isoData.localTime,
59 | templ.getField(12).type,
60 | templ.getField(12).length
61 | )
62 | }
63 | /** Set Local Date **/
64 | if (isoData.localDate != null && !StringUtil.isEmpty(isoData.localDate)) {
65 | message.setValue(
66 | 13,
67 | isoData.localDate,
68 | templ.getField(13).type,
69 | templ.getField(13).length
70 | )
71 | }
72 | /** Set Card Acceptor Terminal Code or Terminal ID **/
73 | if (isoData.cardAcceptorTerminalId != null && !StringUtil.isEmpty(isoData.cardAcceptorTerminalId)) {
74 | val cardAcceptorTerminalIdLength: String = StringUtil.leftPadding('0', 3, isoData.cardAcceptorTerminalId!!.length.toString())
75 | managementDataOne
76 | .append(cardAcceptorTerminalIdLength)
77 | .append(isoData.cardAcceptorTerminalId)
78 | message.setValue(
79 | 41,
80 | isoData.cardAcceptorTerminalId,
81 | templ.getField(41).type,
82 | templ.getField(41).length
83 | )
84 | }
85 | /** Set Management Data One **/
86 | if (isoData.managementDataOnePrivate != null && !StringUtil.isEmpty(isoData.managementDataOnePrivate)) {
87 | message.setValue(
88 | 62,
89 | isoData.managementDataOnePrivate,
90 | templ.getField(62).type,
91 | isoData.managementDataOnePrivate!!.length)
92 | } else {
93 | managementDataOne
94 | .append("09").append(appVersionLen).append(appVersion)
95 | .append("10").append(paymentChannelModeLen).append(paymentChannelMode)
96 | .append("11").append(callHomeCommentsLen).append(callHomeComments)
97 | .append("12").append(commsServiceProviderLen).append(commsServiceProvider)
98 | message.setValue(
99 | 62,
100 | managementDataOne.toString(),
101 | templ.getField(62).type,
102 | managementDataOne.toString().length)
103 | }
104 | /** Set Primary Message Hash **/
105 | if (!StringUtil.isEmpty(terminalSessionKey)) {
106 | message.setValue(
107 | 64,
108 | String(byteArrayOf(0x0)),
109 | templ.getField(64).type,
110 | templ.getField(64).length
111 | )
112 | val bytes = message.writeData()
113 | val length = bytes.size
114 | val temp = ByteArray(length - 64)
115 | if (length >= 64) {
116 | System.arraycopy(bytes, 0, temp, 0, length - 64)
117 | }
118 | val hashValue: String = KeyUtil().getMac(terminalSessionKey, temp)
119 | message.setValue(
120 | 64,
121 | hashValue,
122 | templ.getField(64).type,
123 | templ.getField(64).length
124 | )
125 | } else if (isoData.primaryMessageHashValue != null && !StringUtil.isEmpty(isoData.primaryMessageHashValue)){
126 | message.setValue(
127 | 64,
128 | isoData.primaryMessageHashValue,
129 | templ.getField(64).type,
130 | templ.getField(64).length
131 | )
132 | }
133 | return ISOMessage(message)
134 | }
135 |
136 | }
--------------------------------------------------------------------------------
/emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/ISOMessageBuilder.kt:
--------------------------------------------------------------------------------
1 | package com.subsidian.emvcardmanager.builders
2 |
3 | import android.content.Context
4 | import com.solab.iso8583.IsoMessage
5 | import com.solab.iso8583.MessageFactory
6 | import com.solab.iso8583.parse.ConfigParser
7 | import com.subsidian.emvcardmanager.builders.transactions.*
8 | import com.subsidian.emvcardmanager.entities.ISOData
9 | import com.subsidian.emvcardmanager.entities.ISOMessage
10 | import com.subsidian.emvcardmanager.exceptions.ISOException
11 | import com.subsidian.emvcardmanager.utils.*
12 | import java.io.StringReader
13 | import java.nio.charset.StandardCharsets
14 |
15 |
16 | object ISOMessageBuilder {
17 |
18 | var messageFactoryInit = false
19 | var messageFactory: MessageFactory? = null
20 | var sessionKeyInit = false
21 | var clearTsk: String? = null
22 |
23 | private var packBuilder: PackBuilder? = null
24 |
25 | fun packMessage(context: Context, terminalMasterKey: String = "", terminalSessionKey: String = ""): PackBuilder {
26 | packBuilder = PackBuilder(context,terminalMasterKey, terminalSessionKey)
27 | return packBuilder!!
28 | }
29 |
30 | class PackBuilder(context: Context, private val terminalMasterKey: String = "", private val terminalSessionKey: String = "") {
31 |
32 | var message: ISOMessage? = null
33 |
34 | init {
35 | try {
36 | if (!messageFactoryInit) {
37 | val xml = "NIBSS_PACKAGER.xml"
38 | val filesArray: Array? = AssetUtil.getFilesArrayFromAssets(
39 | context, "PACKAGER")
40 | for (file in filesArray!!) {
41 | if (file.contains(xml)) {
42 | val data: ByteArray? = AssetUtil.getFromAssets(context, file)
43 | val string = String(data!!)
44 | val stringReader = StringReader(string)
45 | messageFactory = ConfigParser.createFromReader(stringReader)
46 | messageFactory!!.isUseBinaryBitmap = false
47 | messageFactory!!.characterEncoding = StandardCharsets.UTF_8.name()
48 | messageFactoryInit = true
49 | }
50 | }
51 | }
52 | if (!sessionKeyInit && (!StringUtil.isEmpty(terminalMasterKey) && !StringUtil.isEmpty(terminalSessionKey))) {
53 | // val clearTmk: String = decryptBase64StringWithRSA(tmk)
54 | clearTsk = KeyUtil().decryptWithDES(terminalMasterKey, terminalSessionKey)
55 | sessionKeyInit = true
56 | }
57 | } catch (ex: Exception) {
58 | ex.printStackTrace()
59 | }
60 | }
61 |
62 | @Throws(Exception::class)
63 | fun createTMKDownloadRequest(isoData: ISOData): PackBuilder {
64 | return echoMessage(TMKRequestBuilder.build(isoData, messageFactory!!))
65 | }
66 |
67 | @Throws(Exception::class)
68 | fun createTPKDownloadRequest(isoData: ISOData): PackBuilder {
69 | return echoMessage(TPKRequestBuilder.build(isoData, messageFactory!!))
70 | }
71 |
72 | @Throws(Exception::class)
73 | fun createTSKDownloadRequest(isoData: ISOData): PackBuilder {
74 | return echoMessage(TSKRequestBuilder.build(isoData, messageFactory!!))
75 | }
76 |
77 | @kotlin.jvm.Throws(Exception::class)
78 | fun createAIDDownloadRequest(isoData: ISOData): PackBuilder {
79 | return echoMessage(AIDRequestBuilder.build(isoData, messageFactory!!, terminalSessionKey))
80 | }
81 |
82 | @kotlin.jvm.Throws(Exception::class)
83 | fun createCAPKDownloadRequest(isoData: ISOData): PackBuilder {
84 | return echoMessage(CAPKRequestBuilder.build(isoData, messageFactory!!, terminalSessionKey))
85 | }
86 |
87 | @kotlin.jvm.Throws(Exception::class)
88 | fun createIPEKTrackTwoRequest(isoData: ISOData): PackBuilder {
89 | return echoMessage(IPEKTrackTwoRequestBuilder.build(isoData, messageFactory!!, terminalSessionKey))
90 | }
91 |
92 | @kotlin.jvm.Throws(Exception::class)
93 | fun createIPEKEMVRequest(isoData: ISOData): PackBuilder {
94 | return echoMessage(IPEKEMVRequestBuilder.build(isoData, messageFactory!!, terminalSessionKey))
95 | }
96 |
97 | @kotlin.jvm.Throws(Exception::class)
98 | fun createTerminalParameterDownloadRequest(isoData: ISOData): PackBuilder {
99 | return echoMessage(TerminalParameterRequestBuilder.build(isoData, messageFactory!!, terminalSessionKey))
100 | }
101 |
102 | @kotlin.jvm.Throws(Exception::class)
103 | fun createDailyReportDownloadRequest(isoData: ISOData): PackBuilder {
104 | return echoMessage(DailyReportRequestBuilder.build(isoData, messageFactory!!, terminalSessionKey))
105 | }
106 |
107 | @kotlin.jvm.Throws(Exception::class)
108 | fun createCallHomeRequest(isoData: ISOData): PackBuilder {
109 | return echoMessage(CallHomeRequestBuilder.build(isoData, messageFactory!!, terminalSessionKey))
110 | }
111 |
112 | @Throws(Exception::class)
113 | fun createFinancialTransactionRequest(isoData: ISOData): PackBuilder {
114 | return echoMessage(FinancialTransactionRequestBuilder.build(isoData, messageFactory!!, terminalSessionKey))
115 | }
116 |
117 | @Throws(Exception::class)
118 | private fun echoMessage(message: ISOMessage) : PackBuilder {
119 | PrintUtil.dumpIsoMessages(message, true)
120 | this.message = message
121 | return this
122 | }
123 |
124 | }
125 |
126 |
127 | fun unpackMessage(context: Context, data: ByteArray): UnpackBuilder {
128 | return UnpackBuilder(context, data)
129 | }
130 |
131 | class UnpackBuilder(context: Context, private val data: ByteArray?) {
132 |
133 | init {
134 | try {
135 | if (!messageFactoryInit) {
136 | val xml = "NIBSS_PACKAGER.xml"
137 | val filesArray: Array? = AssetUtil.getFilesArrayFromAssets(
138 | context, "PACKAGER")
139 | for (file in filesArray!!) {
140 | if (file.contains(xml)) {
141 | val data: ByteArray? = AssetUtil.getFromAssets(context, file)
142 | val string = String(data!!)
143 | val stringReader = StringReader(string)
144 | messageFactory = ConfigParser.createFromReader(stringReader)
145 | messageFactory!!.isUseBinaryBitmap = false
146 | messageFactory!!.characterEncoding = StandardCharsets.UTF_8.name()
147 | messageFactoryInit = true
148 | }
149 | }
150 | }
151 | } catch (ex: Exception) {
152 | ex.printStackTrace()
153 | }
154 | }
155 |
156 | @Throws(ISOException::class)
157 | fun unpack(): ISOData {
158 | return try {
159 | val isoMessage = ISOMessage(messageFactory!!.parseMessage(data, 0) as IsoMessage)
160 | PrintUtil.dumpIsoMessages(isoMessage, false)
161 | VariableCheckUtil.unpackISOMessage(isoMessage)
162 | } catch (ex: Exception) {
163 | ex.printStackTrace()
164 | ISOData()
165 | }
166 | }
167 | }
168 |
169 | }
--------------------------------------------------------------------------------
/emvcardmanager/src/main/java/com/subsidian/emvcardmanager/builders/ISOClientBuilder.kt:
--------------------------------------------------------------------------------
1 | package com.subsidian.emvcardmanager.builders
2 |
3 | import com.solab.iso8583.IsoMessage
4 | import com.subsidian.emvcardmanager.exceptions.ISOClientException
5 | import com.subsidian.emvcardmanager.handlers.IOSocketHandler
6 | import com.subsidian.emvcardmanager.handlers.NIOSocketHandler
7 | import com.subsidian.emvcardmanager.handlers.SSLHandler
8 | import com.subsidian.emvcardmanager.interfaces.ISOClient
9 | import com.subsidian.emvcardmanager.interfaces.ISOClientEventListener
10 | import com.subsidian.emvcardmanager.interfaces.SSLProtocol
11 | import com.subsidian.emvcardmanager.interfaces.SocketHandler
12 | import kotlinx.coroutines.Dispatchers
13 | import kotlinx.coroutines.GlobalScope
14 | import kotlinx.coroutines.launch
15 | import java.io.IOException
16 | import java.nio.ByteBuffer
17 | import java.util.*
18 |
19 | object ISOClientBuilder {
20 |
21 | private var clientBuilder: ClientBuilder? = null
22 |
23 | fun createSocket(host: String, port: Int): ClientBuilder {
24 | clientBuilder = ClientBuilder(host, port)
25 | return clientBuilder!!
26 | }
27 |
28 | /**
29 | * ClientBuilder
30 | */
31 | class ClientBuilder(host: String, port: Int) {
32 |
33 | private val client: DefaultISOClient = DefaultISOClient()
34 |
35 | /**
36 | * Sending with NIO (false) or Blocking IO (true)
37 | * @param blocking:true
38 | * @return [ClientBuilder]
39 | */
40 | fun configureBlocking(blocking: Boolean): ClientBuilder {
41 | client.setBlocking(blocking)
42 | return this
43 | }
44 |
45 | /**
46 | * Enable sending over SSL/TLS
47 | * @return [ClientBuilder]
48 | */
49 | fun enableSSL(): SSLProtocol {
50 | return client.enableSSL(SSLHandler(this))
51 | }
52 |
53 | /**
54 | * Build ISOClient for sending label
55 | * @return [ClientBuilder]
56 | */
57 | fun build(): ISOClient {
58 | return client
59 | }
60 |
61 | /**
62 | * set Timeout for read from socket
63 | * @param millisecond timeout in millisecond
64 | * @return [ClientBuilder]
65 | */
66 | fun setReadTimeout(millisecond: Int): ClientBuilder {
67 | client.setReadTimeout(millisecond)
68 | return this
69 | }
70 |
71 | /**
72 | * Set Message length in Byte
73 | * @param bytes default: 2 byte
74 | * @return [ClientBuilder]
75 | */
76 | fun length(bytes: Int): ClientBuilder {
77 | client.setLength(bytes)
78 | return this
79 | }
80 |
81 | /**
82 | * Set event listener for dispatch events
83 | * @param eventListener Implementation of [ISOClientEventListener]
84 | * @return [ClientBuilder]
85 | */
86 | fun setEventListener(eventListener: ISOClientEventListener?): ClientBuilder {
87 | if (eventListener != null) client.setEventListener(eventListener)
88 | return this
89 | }
90 |
91 | /**
92 | * Create ISO Client after initializing
93 | * @param host socket Host
94 | * @param port socket ip
95 | */
96 | init {
97 | client.setSocketAddress(host, port)
98 | }
99 | }
100 |
101 | class DefaultISOClient internal constructor() : ISOClient {
102 |
103 | private var sslHandler: SSLHandler? = null
104 | private var socketHandler: SocketHandler? = null
105 | private var buffer: ByteBuffer? = null
106 | private var blocking = true
107 |
108 | @Volatile
109 | private var connected = false
110 | private var host: String = ""
111 | private var port = 0
112 | private var readTimeout = 60000
113 | private var length = 2
114 | private val lock = Any()
115 | private var isoClientEventListener: ISOClientEventListener?
116 |
117 | @Throws(ISOClientException::class, IOException::class)
118 | override fun connect() {
119 | GlobalScope.launch(Dispatchers.IO) {
120 | isoClientEventListener!!.connecting()
121 | if (sslHandler != null) socketHandler!!.init(
122 | host,
123 | port,
124 | isoClientEventListener,
125 | sslHandler
126 | ) else socketHandler!!.init(host, port, isoClientEventListener)
127 | socketHandler!!.setReadTimeout(readTimeout)
128 | connected = true
129 | isoClientEventListener!!.connected()
130 | }
131 | }
132 |
133 | override fun disconnect() {
134 | if (socketHandler != null) socketHandler!!.close()
135 | if (buffer != null) {
136 | buffer!!.flip()
137 | buffer!!.put(ByteBuffer.allocate(buffer!!.limit()))
138 | buffer = null
139 | }
140 | connected = false
141 | isoClientEventListener!!.disconnected()
142 | }
143 |
144 | private fun initBuffer(isoMessage: IsoMessage): ByteBuffer {
145 | val len = isoMessage.writeData().size
146 | buffer = ByteBuffer.allocate(len + length)
147 | if (length > 0) {
148 | val mlen = ByteBuffer.allocate(4).putInt(len).array()
149 | buffer!!.put(Arrays.copyOfRange(mlen, 2, 4))
150 | }
151 | buffer!!.put(isoMessage.writeData())
152 | return buffer!!
153 | }
154 |
155 | @Throws(ISOClientException::class, IOException::class)
156 | override fun sendMessageSync(packBuilder: ISOMessageBuilder.PackBuilder): ByteArray {
157 | var result: ByteArray = ByteArray(0)
158 | synchronized(lock) {
159 | if (socketHandler == null) throw ISOClientException("Client handler init failed, unable to connect to the server!")
160 | if (!isConnected()) throw ISOClientException("Client unable to connect to the server!")
161 | val buffer: ByteBuffer = if (packBuilder.message != null){
162 | initBuffer(packBuilder.message?.getMessage()!!)
163 | } else {
164 | ByteBuffer.allocate(0)
165 | }
166 | result = socketHandler!!.sendMessageSync(buffer, length)!!
167 | }
168 | return result
169 | }
170 |
171 |
172 | override fun isConnected(): Boolean {
173 | return socketHandler != null && socketHandler!!.isConnected()
174 | }
175 |
176 | override fun isClosed(): Boolean {
177 | return socketHandler != null && socketHandler!!.isClosed()
178 | }
179 |
180 | override fun setEventListener(isoClientEventListener: ISOClientEventListener?) {
181 | this.isoClientEventListener = isoClientEventListener
182 | }
183 |
184 | fun setSocketAddress(host: String, port: Int) {
185 | this.host = host
186 | this.port = port
187 | }
188 |
189 | fun enableSSL(sslHandler: SSLHandler): SSLHandler {
190 | this.sslHandler = sslHandler
191 | return sslHandler
192 | }
193 |
194 | fun setBlocking(blocking: Boolean) {
195 | this.blocking = blocking
196 | }
197 |
198 | fun setReadTimeout(readTimeout: Int) {
199 | this.readTimeout = readTimeout
200 | }
201 |
202 | fun setLength(bytes: Int) {
203 | this.length = bytes
204 | }
205 |
206 | init {
207 | socketHandler = if (blocking) {
208 | IOSocketHandler()
209 | } else {
210 | NIOSocketHandler()
211 | }
212 | isoClientEventListener = EmptyISOClientEventListener()
213 | }
214 | }
215 |
216 | private class EmptyISOClientEventListener : ISOClientEventListener {
217 | override fun connecting() {}
218 | override fun connected() {}
219 | override fun connectionFailed() {}
220 | override fun connectionClosed() {}
221 | override fun connectionTimeout() {}
222 | override fun disconnected() {}
223 | override fun beforeSendingMessage() {}
224 | override fun afterSendingMessage() {}
225 | override fun onReceiveData() {}
226 | override fun beforeReceiveResponse() {}
227 | override fun afterReceiveResponse() {}
228 | }
229 | }
--------------------------------------------------------------------------------
/emvcardmanager/src/main/java/com/subsidian/emvcardmanager/handlers/NIOSocketHandler.kt:
--------------------------------------------------------------------------------
1 | package com.subsidian.emvcardmanager.handlers
2 |
3 | import kotlin.Throws
4 | import com.subsidian.emvcardmanager.exceptions.ISOClientException
5 | import com.subsidian.emvcardmanager.interfaces.ISOClientEventListener
6 | import com.subsidian.emvcardmanager.interfaces.SocketHandler
7 | import com.subsidian.emvcardmanager.utils.StringUtil
8 | import kotlinx.coroutines.Dispatchers
9 | import kotlinx.coroutines.GlobalScope
10 | import kotlinx.coroutines.launch
11 | import java.io.IOException
12 | import java.lang.Exception
13 | import java.lang.IllegalStateException
14 | import java.net.InetSocketAddress
15 | import java.net.SocketException
16 | import java.nio.ByteBuffer
17 | import java.nio.channels.SocketChannel
18 | import java.util.*
19 | import javax.net.ssl.SSLEngine
20 | import javax.net.ssl.SSLEngineResult
21 | import javax.net.ssl.SSLException
22 |
23 | class NIOSocketHandler : SocketHandler {
24 | private var socketChannel: SocketChannel? = null
25 | private var myAppData: ByteBuffer? = null
26 | private var myNetData: ByteBuffer? = null
27 | private var peerAppData: ByteBuffer? = null
28 | private var peerNetData: ByteBuffer? = null
29 | private var engine: SSLEngine? = null
30 | private var sslHandler: SSLHandler? = null
31 |
32 | @Throws(ISOClientException::class)
33 | override fun init(
34 | host: String,
35 | port: Int,
36 | isoClientEventListener: ISOClientEventListener?,
37 | sslHandler: SSLHandler?
38 | ) {
39 | try {
40 | this@NIOSocketHandler.sslHandler = sslHandler
41 | engine = sslHandler!!.context.createSSLEngine(host, port)
42 | engine!!.useClientMode = true
43 | engine!!.needClientAuth = false
44 | socketChannel = SocketChannel.open()
45 | socketChannel!!.configureBlocking(false)
46 | socketChannel!!.connect(InetSocketAddress(host, port))
47 | while (!socketChannel!!.finishConnect()) {
48 | }
49 |
50 | // Create byte buffers to use for holding application and encoded data
51 | val session = engine!!.session
52 | myAppData = ByteBuffer.allocate(session.applicationBufferSize)
53 | myNetData = ByteBuffer.allocate(session.packetBufferSize)
54 | peerAppData = ByteBuffer.allocate(session.applicationBufferSize)
55 | peerNetData = ByteBuffer.allocate(session.packetBufferSize)
56 | val connected = sslHandler.doHandshake(
57 | socketChannel!!,
58 | engine!!,
59 | myNetData!!,
60 | peerNetData!!,
61 | peerAppData!!,
62 | myAppData
63 | )
64 | if (!connected) throw ISOClientException("Handshake not performed well")
65 | postInit()
66 | } catch (e: Exception) {
67 | throw ISOClientException(e)
68 | }
69 | }
70 |
71 | @Throws(IOException::class)
72 | override fun init(host: String, port: Int, isoClientEventListener: ISOClientEventListener?) {
73 | socketChannel = SocketChannel.open()
74 | socketChannel!!.configureBlocking(false)
75 | socketChannel!!.connect(InetSocketAddress(host, port))
76 | while (!socketChannel!!.finishConnect()) {
77 | }
78 | myAppData = ByteBuffer.allocate(1024)
79 | postInit()
80 | }
81 |
82 | @Throws(SocketException::class)
83 | private fun postInit() {
84 | socketChannel!!.socket().soTimeout = 60000
85 | }
86 |
87 | @Throws(IOException::class)
88 | override fun sendMessageSync(buffer: ByteBuffer, length: Int): ByteArray {
89 | return if (sslHandler != null) {
90 | val data = sendMessageSyncOverSsl(buffer)
91 | Arrays.copyOfRange(data, if (length > 0) length else 0, data.size)
92 | } else {
93 | myAppData!!.clear()
94 | myAppData!!.put(buffer.array())
95 | myAppData!!.flip()
96 | while (myAppData!!.hasRemaining()) {
97 | socketChannel!!.write(myAppData)
98 | }
99 | myAppData!!.clear()
100 | myAppData!!.compact()
101 | myAppData!!.flip()
102 | var r: Int
103 | do {
104 | r = socketChannel!!.read(myAppData)
105 | } while (myAppData!!.remaining() >= 0 && r == 0)
106 | if (myAppData!!.position() > length) Arrays.copyOfRange(
107 | myAppData!!.array(),
108 | if (length > 0) length else 0,
109 | myAppData!!.position()
110 | ) else ByteArray(0)
111 | }
112 | }
113 |
114 | @Throws(IOException::class)
115 | private fun sendMessageSyncOverSsl(buffer: ByteBuffer): ByteArray {
116 | write(buffer)
117 | return read()
118 | }
119 |
120 | @Throws(IOException::class)
121 | private fun write(buffer: ByteBuffer) {
122 | myAppData!!.clear()
123 | myAppData!!.put(buffer.array())
124 | myAppData!!.flip()
125 | while (myAppData!!.hasRemaining()) {
126 | // The loop has a meaning for (outgoing) messages larger than 16KB.
127 | // Every wrap call will remove 16KB from the original label and send it to the remote peer.
128 | myNetData!!.clear()
129 | val result = engine!!.wrap(myAppData, myNetData)
130 | when (result.status) {
131 | SSLEngineResult.Status.OK -> {
132 | myNetData!!.flip()
133 | while (myNetData!!.hasRemaining()) {
134 | socketChannel!!.write(myNetData)
135 | }
136 | println("Message sent to the server: " + StringUtil.fromByteArray(myAppData!!.array()))
137 | }
138 | SSLEngineResult.Status.BUFFER_OVERFLOW -> myNetData =
139 | sslHandler!!.enlargePacketBuffer(
140 | engine!!, myNetData!!
141 | )
142 | SSLEngineResult.Status.BUFFER_UNDERFLOW -> throw SSLException("Buffer underflow occured after a wrap. I don't think we should ever get here.")
143 | SSLEngineResult.Status.CLOSED -> {
144 | close()
145 | return
146 | }
147 | else -> throw IllegalStateException("Invalid SSL status: " + result.status)
148 | }
149 | }
150 | }
151 |
152 | @Throws(IOException::class)
153 | private fun read(): ByteArray {
154 | var response = ByteArray(0)
155 | peerNetData!!.clear()
156 | val waitToReadMillis = 50
157 | var exitReadLoop = false
158 | while (!exitReadLoop) {
159 | val bytesRead = socketChannel!!.read(peerNetData)
160 | if (bytesRead > 0) {
161 | peerNetData!!.flip()
162 | while (peerNetData!!.hasRemaining()) {
163 | peerAppData!!.clear()
164 | val result = engine!!.unwrap(peerNetData, peerAppData)
165 | when (result.status) {
166 | SSLEngineResult.Status.OK -> {
167 | peerAppData!!.flip()
168 | val resp = ByteArray(peerAppData!!.limit())
169 | var i = 0
170 | while (i < resp.size) {
171 | resp[i] = peerAppData!![i]
172 | i++
173 | }
174 | response = resp
175 | exitReadLoop = true
176 | }
177 | SSLEngineResult.Status.BUFFER_OVERFLOW -> peerAppData =
178 | sslHandler!!.enlargeApplicationBuffer(
179 | engine!!, peerAppData!!
180 | )
181 | SSLEngineResult.Status.BUFFER_UNDERFLOW -> peerNetData =
182 | sslHandler!!.handleBufferUnderflow(
183 | engine!!, peerNetData!!
184 | )
185 | SSLEngineResult.Status.CLOSED -> {
186 | close()
187 | return response
188 | }
189 | else -> throw IllegalStateException("Invalid SSL status: " + result.status)
190 | }
191 | }
192 | } else if (bytesRead < 0) {
193 | engine!!.closeInbound()
194 | return response
195 | }
196 | try {
197 | Thread.sleep(waitToReadMillis.toLong())
198 | } catch (e: InterruptedException) {
199 | e.printStackTrace()
200 | }
201 | }
202 | return response
203 | }
204 |
205 | override fun close() {
206 | try {
207 | if (socketChannel != null) socketChannel!!.close()
208 | if (myAppData != null) {
209 | myAppData!!.compact()
210 | myAppData!!.clear()
211 | myAppData = null
212 | }
213 | if (myNetData != null) {
214 | myNetData!!.compact()
215 | myNetData!!.clear()
216 | myNetData = null
217 | }
218 | } catch (e: IOException) {
219 | e.printStackTrace()
220 | }
221 | }
222 |
223 | @Throws(SocketException::class)
224 | override fun setReadTimeout(readTimeout: Int) {
225 | socketChannel!!.socket().soTimeout = 60000
226 | }
227 |
228 | override fun isConnected(): Boolean {
229 | return socketChannel != null && socketChannel!!.isConnected
230 | }
231 |
232 | override fun isClosed(): Boolean {
233 | return socketChannel == null || socketChannel!!.socket().isClosed
234 | }
235 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/subsidian/emvcardsdkdemo/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.subsidian.emvcardsdkdemo
2 |
3 | import android.os.Bundle
4 | import android.util.Log
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.subsidian.emvcardmanager.builders.ISOClientBuilder
7 | import com.subsidian.emvcardmanager.builders.ISOMessageBuilder
8 | import com.subsidian.emvcardmanager.interfaces.ISOClient
9 | import com.subsidian.emvcardmanager.interfaces.ISOClientEventListener
10 | import com.subsidian.emvcardsdkdemo.utils.transaction.*
11 |
12 |
13 | class MainActivity : AppCompatActivity() {
14 |
15 | var client: ISOClient? = null
16 |
17 | override fun onCreate(savedInstanceState: Bundle?) {
18 | super.onCreate(savedInstanceState)
19 | setContentView(R.layout.activity_main)
20 |
21 | try {
22 | client = ISOClientBuilder
23 | .createSocket("arca-pos.qa.arca-payments.network", 11000)
24 | .configureBlocking(false)
25 | .enableSSL()
26 | .setSSLProtocol("SSL") //TLS, SSL, TLSv1.2
27 | .setKeyManagers(arrayOf())
28 | .setTrustManagers(arrayOf())
29 | .setEventListener(clientEventListener)
30 | .build()
31 | /** Connect the client socket to the server**/
32 | client!!.connect()
33 | /** Send and Read data response **/
34 | // val response: String = Arrays.toString(
35 | // client.sendMessageSync(
36 | // ISOMessageBuilder.createMessage(applicationContext)
37 | // .createTMKDownloadRequest()
38 | // )
39 | // )
40 | // ISOMessageBuilder.unpackMessage(applicationContext, client.sendMessageSync(
41 | // ISOMessageBuilder.createMessage(applicationContext)
42 | // .createTMKDownloadRequest()
43 | // )).build()
44 | // println("response = $response")
45 | /** Disconnect from the client socket **/
46 | // client.disconnect()
47 | } catch (e: Exception) {
48 | e.printStackTrace()
49 | }
50 | }
51 |
52 |
53 | private val clientEventListener = object : ISOClientEventListener {
54 | override fun connecting() {
55 | println("${this.javaClass.simpleName} ==> Client Connecting.")
56 | }
57 | override fun connected() {
58 | println("${this.javaClass.simpleName} ==> Client Connected.")
59 | if (client != null) {
60 | /** TMK **/
61 | testTMKDownload()
62 | /** TPK **/
63 | testTPKDownload()
64 | /** TSK **/
65 | testTSKDownload()
66 | /** AID **/
67 | testAIDDownload()
68 | /** CAPK **/
69 | testCAPKDownload()
70 | /** Terminal Parameter **/
71 | testTerminalParameterDownload()
72 | /** Daily Report **/
73 | testDailyReportDownload()
74 | /** Call Home **/
75 | testCallHomeDownload()
76 | /** IPEK EMV **/
77 | testIPEKEMVDownload()
78 | /** IPEK Track Two **/
79 | testIPEKTrackTwoDownload()
80 | /** Purchase **/
81 | testPurchaseTransaction()
82 | /** Reversal **/
83 | testReversalTransaction()
84 | /** Disconnect from the client socket **/
85 | client!!.disconnect()
86 | }
87 | }
88 | override fun connectionFailed() {
89 | println("${this.javaClass.simpleName} ==> Client Connection Failed.")
90 | }
91 | override fun connectionClosed() {
92 | println("${this.javaClass.simpleName} ==> Client Connection Closed.")
93 | }
94 | override fun connectionTimeout() {
95 | println("${this.javaClass.simpleName} ==> Client Connection Timeout.")
96 | }
97 | override fun disconnected() {
98 | println("${this.javaClass.simpleName} ==> Client Disconnected.")
99 | }
100 | override fun beforeSendingMessage() {
101 | println("${this.javaClass.simpleName} ==> Client Before Sending Message.")
102 | }
103 | override fun afterSendingMessage() {
104 | println("${this.javaClass.simpleName} ==> Client After Sending Message.")
105 | }
106 | override fun onReceiveData() {
107 | println("${this.javaClass.simpleName} ==> Client Message Received.")
108 | }
109 | override fun beforeReceiveResponse() {
110 | println("${this.javaClass.simpleName} ==> Client Before Message Receive.")
111 | }
112 | override fun afterReceiveResponse() {
113 | println("${this.javaClass.simpleName} ==> Client After Message Receive.")
114 | }
115 | }
116 |
117 | /**
118 | * Test TMK Download Transaction
119 | */
120 | fun testTMKDownload() {
121 | ISOMessageBuilder.unpackMessage(
122 | applicationContext,
123 | client!!.sendMessageSync(
124 | ISOMessageBuilder.packMessage(applicationContext, "", "")
125 | .createTMKDownloadRequest(TMKRequest.build())
126 | )
127 | ).unpack()
128 | }
129 |
130 | /**
131 | * Test TPK Download Transaction
132 | */
133 | fun testTPKDownload() {
134 | ISOMessageBuilder.unpackMessage(
135 | applicationContext, client!!.sendMessageSync(
136 | ISOMessageBuilder.packMessage(applicationContext, "", "")
137 | .createTPKDownloadRequest(TPKRequest.build())
138 | )
139 | ).unpack()
140 | }
141 |
142 | /**
143 | * Test TSK Download Transaction
144 | */
145 | fun testTSKDownload() {
146 | ISOMessageBuilder.unpackMessage(
147 | applicationContext, client!!.sendMessageSync(
148 | ISOMessageBuilder.packMessage(applicationContext, "", "")
149 | .createTSKDownloadRequest(TSKRequest.build())
150 | )
151 | ).unpack()
152 | }
153 |
154 | /**
155 | * Test AID Download Transaction
156 | */
157 | fun testAIDDownload() {
158 | ISOMessageBuilder.unpackMessage(
159 | applicationContext, client!!.sendMessageSync(
160 | ISOMessageBuilder.packMessage(applicationContext, "", "")
161 | .createAIDDownloadRequest(AIDRequest.build())
162 | )
163 | ).unpack()
164 | }
165 |
166 | /**
167 | * Test CAPK Download Transaction
168 | */
169 | fun testCAPKDownload() {
170 | ISOMessageBuilder.unpackMessage(
171 | applicationContext, client!!.sendMessageSync(
172 | ISOMessageBuilder.packMessage(applicationContext, "", "")
173 | .createCAPKDownloadRequest(CAPKRequest.build())
174 | )
175 | ).unpack()
176 | }
177 |
178 | /**
179 | * Test Terminal Parameter Download Transaction
180 | */
181 | fun testTerminalParameterDownload() {
182 | ISOMessageBuilder.unpackMessage(
183 | applicationContext, client!!.sendMessageSync(
184 | ISOMessageBuilder.packMessage(applicationContext, "", "")
185 | .createTerminalParameterDownloadRequest(TerminalParameterRequest.build())
186 | )
187 | ).unpack()
188 | }
189 |
190 | /**
191 | * Test Daily Report Download Transaction
192 | */
193 | fun testDailyReportDownload() {
194 | ISOMessageBuilder.unpackMessage(
195 | applicationContext, client!!.sendMessageSync(
196 | ISOMessageBuilder.packMessage(applicationContext, "", "")
197 | .createDailyReportDownloadRequest(DailyReportRequest.build())
198 | )
199 | ).unpack()
200 | }
201 |
202 | /**
203 | * Test Terminal Parameter Download Transaction
204 | */
205 | fun testCallHomeDownload() {
206 | ISOMessageBuilder.unpackMessage(
207 | applicationContext, client!!.sendMessageSync(
208 | ISOMessageBuilder.packMessage(applicationContext, "", "")
209 | .createCallHomeRequest(CallHomeRequest.build())
210 | )
211 | ).unpack()
212 | }
213 |
214 | /**
215 | * Test IPEK EMV Download Transaction
216 | */
217 | fun testIPEKEMVDownload() {
218 | ISOMessageBuilder.unpackMessage(
219 | applicationContext, client!!.sendMessageSync(
220 | ISOMessageBuilder.packMessage(applicationContext, "", "")
221 | .createIPEKEMVRequest(IPEKEMVRequest.build())
222 | )
223 | ).unpack()
224 | }
225 |
226 | /**
227 | * Test IPEK Track Two Download Transaction
228 | */
229 | fun testIPEKTrackTwoDownload() {
230 | ISOMessageBuilder.unpackMessage(
231 | applicationContext, client!!.sendMessageSync(
232 | ISOMessageBuilder.packMessage(applicationContext, "", "")
233 | .createIPEKTrackTwoRequest(IPEKTrackTwoRequest.build())
234 | )
235 | ).unpack()
236 | }
237 |
238 | /**
239 | * Test Purchase Transaction
240 | */
241 | fun testPurchaseTransaction(){
242 | Log.d(this.javaClass.simpleName,ISOMessageBuilder.unpackMessage(
243 | applicationContext, client!!.sendMessageSync(
244 | ISOMessageBuilder.packMessage(applicationContext, "", "")
245 | .createFinancialTransactionRequest(PurchaseRequest.build())
246 | )
247 | ).unpack().toString())
248 | }
249 |
250 | /**
251 | * Test Reversal Transaction
252 | */
253 | fun testReversalTransaction(){
254 | Log.d(this.javaClass.simpleName,ISOMessageBuilder.unpackMessage(
255 | applicationContext, client!!.sendMessageSync(
256 | ISOMessageBuilder.packMessage(applicationContext, "", "")
257 | .createFinancialTransactionRequest(ReversalRequest.build())
258 | )
259 | ).unpack().toString())
260 | }
261 |
262 | }
--------------------------------------------------------------------------------