diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7baf399f7f..a4ab1eb987 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,7 +10,11 @@ All notable changes to this project will be documented in this file.
## [0.75.0]
### Changed
* ui: update fiserv 3D's flow. The user now has to enter additional information to add a credit card.
- * instead of using the CreditCardInput it is now required to use the FiservInput instead.
+ * instead of using the `CreditCardInput` it is now required to use the `FiservInput` instead.
+* ui: update datatrans 3D's flow. The user now has to enter additional information to add a credit card.
+ * the new flow can be integrated via the new `SnabbleUi.Event` `SHOW_DATATRANS_INPUT`
+ * u can directly integrate the `DatatransFragment` as navigation target for this event
+
## [0.74.0]
### Added
* core: add new and update existing user agent headers
diff --git a/build.gradle.kts b/build.gradle.kts
index c6697346d5..5f30647f8d 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -33,6 +33,7 @@ allprojects {
repositories {
google()
mavenCentral()
+ maven(url = "https://raw.githubusercontent.com/snabble/maven-repository/releases")
maven(url = "https://datatrans.jfrog.io/artifactory/mobile-sdk/")
maven(url = "https://jitpack.io")
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index ef4fba0143..a3511088dd 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -83,6 +83,7 @@ jakewhartonProcessPhoenix = "com.jakewharton:process-phoenix:2.1.2"
picasso = "com.squareup.picasso:picasso:2.8"
rekisoftLazyWorker = "eu.rekisoft.android.util:LazyWorker:2.1.0"
relex-circleindicator = "me.relex:circleindicator:2.1.6"
+snabble-phoneAuth-countryCodePicker = "io.snabble.phoneauth:countryCodePicker:3.2.2"
squareup-okhttp3-loggingInterceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "com-squareup-okhttp3" }
squareup-okhttp3-okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "com-squareup-okhttp3" }
squareup-okhttp3-tls = { module = "com.squareup.okhttp3:okhttp-tls", version.ref = "com-squareup-okhttp3" }
diff --git a/kotlin-sample/src/main/res/navigation/mobile_navigation.xml b/kotlin-sample/src/main/res/navigation/mobile_navigation.xml
index dc745cb487..9161096493 100644
--- a/kotlin-sample/src/main/res/navigation/mobile_navigation.xml
+++ b/kotlin-sample/src/main/res/navigation/mobile_navigation.xml
@@ -166,7 +166,7 @@
+
diff --git a/ui/src/main/assets/countriesAndStates.json b/ui/src/main/assets/countriesAndStates.json
index d0d50f199e..5eadbbd79f 100644
--- a/ui/src/main/assets/countriesAndStates.json
+++ b/ui/src/main/assets/countriesAndStates.json
@@ -1,33 +1,43 @@
[
{
- "code": "AF"
+ "code": "AF",
+ "numeric": "004"
},
{
- "code": "AL"
+ "code": "AL",
+ "numeric": "008"
},
{
- "code": "DZ"
+ "code": "DZ",
+ "numeric": "012"
},
{
- "code": "AS"
+ "code": "AS",
+ "numeric": "016"
},
{
- "code": "AD"
+ "code": "AD",
+ "numeric": "020"
},
{
- "code": "AO"
+ "code": "AO",
+ "numeric": "024"
},
{
- "code": "AI"
+ "code": "AI",
+ "numeric": "660"
},
{
- "code": "AQ"
+ "code": "AQ",
+ "numeric": "010"
},
{
- "code": "AG"
+ "code": "AG",
+ "numeric": "028"
},
{
"code": "AR",
+ "numeric": "032",
"states": [
{
"code" : "B",
@@ -128,67 +138,88 @@
]
},
{
- "code": "AM"
+ "code": "AM",
+ "numeric": "051"
},
{
- "code": "AW"
+ "code": "AW",
+ "numeric": "533"
},
{
- "code": "AU"
+ "code": "AU",
+ "numeric": "036"
},
{
- "code": "AT"
+ "code": "AT",
+ "numeric": "040"
},
{
- "code": "AZ"
+ "code": "AZ",
+ "numeric": "031"
},
{
- "code": "BS"
+ "code": "BS",
+ "numeric": "044"
},
{
- "code": "BH"
+ "code": "BH",
+ "numeric": "048"
},
{
- "code": "BD"
+ "code": "BD",
+ "numeric": "050"
},
{
- "code": "BB"
+ "code": "BB",
+ "numeric": "052"
},
{
- "code": "BY"
+ "code": "BY",
+ "numeric": "112"
},
{
- "code": "BE"
+ "code": "BE",
+ "numeric": "056"
},
{
- "code": "BZ"
+ "code": "BZ",
+ "numeric": "084"
},
{
- "code": "BJ"
+ "code": "BJ",
+ "numeric": "204"
},
{
- "code": "BM"
+ "code": "BM",
+ "numeric": "060"
},
{
- "code": "BT"
+ "code": "BT",
+ "numeric": "064"
},
{
- "code": "BO"
+ "code": "BO",
+ "numeric": "068"
},
{
- "code": "BQ"
+ "code": "BQ",
+ "numeric": "535"
},
{
- "code": "BA"
+ "code": "BA",
+ "numeric": "070"
},
{
- "code": "BW"
+ "code": "BW",
+ "numeric": "072"
},
{
- "code": "BV"
+ "code": "BV",
+ "numeric": "074"
},
{
"code": "BR",
+ "numeric": "076",
"states": [
{
"code" : "AC",
@@ -301,28 +332,36 @@
]
},
{
- "code": "IO"
+ "code": "IO",
+ "numeric": "086"
},
{
- "code": "BN"
+ "code": "BN",
+ "numeric": "096"
},
{
- "code": "BG"
+ "code": "BG",
+ "numeric": "100"
},
{
- "code": "BF"
+ "code": "BF",
+ "numeric": "854"
},
{
- "code": "BI"
+ "code": "BI",
+ "numeric": "108"
},
{
- "code": "KH"
+ "code": "KH",
+ "numeric": "116"
},
{
- "code": "CM"
+ "code": "CM",
+ "numeric": "120"
},
{
"code": "CA",
+ "numeric": "124",
"states": [
{
"code" : "AB",
@@ -379,22 +418,28 @@
]
},
{
- "code": "CV"
+ "code": "CV",
+ "numeric": "132"
},
{
- "code": "KY"
+ "code": "KY",
+ "numeric": "136"
},
{
- "code": "CF"
+ "code": "CF",
+ "numeric": "140"
},
{
- "code": "TD"
+ "code": "TD",
+ "numeric": "148"
},
{
- "code": "CL"
+ "code": "CL",
+ "numeric": "152"
},
{
"code": "CN",
+ "numeric": "156",
"states": [
{
"code" : "11",
@@ -535,169 +580,224 @@
]
},
{
- "code": "CW"
+ "code": "CW",
+ "numeric": "531"
},
{
- "code": "CX"
+ "code": "CX",
+ "numeric": "162"
},
{
- "code": "CC"
+ "code": "CC",
+ "numeric": "166"
},
{
- "code": "CO"
+ "code": "CO",
+ "numeric": "170"
},
{
- "code": "KM"
+ "code": "KM",
+ "numeric": "174"
},
{
- "code": "CG"
+ "code": "CG",
+ "numeric": "178"
},
{
- "code": "CK"
+ "code": "CK",
+ "numeric": "184"
},
{
- "code": "CR"
+ "code": "CR",
+ "numeric": "188"
},
{
- "code": "CI"
+ "code": "CI",
+ "numeric": "384"
},
{
- "code": "HR"
+ "code": "HR",
+ "numeric": "191"
},
{
- "code": "CU"
+ "code": "CU",
+ "numeric": "192"
},
{
- "code": "CY"
+ "code": "CY",
+ "numeric": "196"
},
{
- "code": "CZ"
+ "code": "CZ",
+ "numeric": "203"
},
{
- "code": "DK"
+ "code": "DK",
+ "numeric": "208"
},
{
- "code": "DJ"
+ "code": "DJ",
+ "numeric": "262"
},
{
- "code": "DM"
+ "code": "DM",
+ "numeric": "212"
},
{
- "code": "DO"
+ "code": "DO",
+ "numeric": "214"
},
{
- "code": "TL"
+ "code": "TL",
+ "numeric": "626"
},
{
- "code": "EC"
+ "code": "EC",
+ "numeric": "218"
},
{
- "code": "EG"
+ "code": "EG",
+ "numeric": "818"
},
{
- "code": "SV"
+ "code": "SV",
+ "numeric": "222"
},
{
- "code": "GQ"
+ "code": "GQ",
+ "numeric": "226"
},
{
- "code": "ER"
+ "code": "ER",
+ "numeric": "232"
},
{
- "code": "EE"
+ "code": "EE",
+ "numeric": "233"
},
{
- "code": "ET"
+ "code": "ET",
+ "numeric": "231"
},
{
- "code": "FK"
+ "code": "FK",
+ "numeric": "238"
},
{
- "code": "FO"
+ "code": "FO",
+ "numeric": "234"
},
{
- "code": "FJ"
+ "code": "FJ",
+ "numeric": "242"
},
{
- "code": "FI"
+ "code": "FI",
+ "numeric": "246"
},
{
- "code": "FR"
+ "code": "FR",
+ "numeric": "250"
},
{
- "code": "GF"
+ "code": "GF",
+ "numeric": "254"
},
{
- "code": "PF"
+ "code": "PF",
+ "numeric": "258"
},
{
- "code": "TF"
+ "code": "TF",
+ "numeric": "260"
},
{
- "code": "GA"
+ "code": "GA",
+ "numeric": "266"
},
{
- "code": "GM"
+ "code": "GM",
+ "numeric": "270"
},
{
- "code": "GE"
+ "code": "GE",
+ "numeric": "268"
},
{
- "code": "DE"
+ "code": "DE",
+ "numeric": "276"
},
{
- "code": "GH"
+ "code": "GH",
+ "numeric": "288"
},
{
- "code": "GI"
+ "code": "GI",
+ "numeric": "292"
},
{
- "code": "GR"
+ "code": "GR",
+ "numeric": "300"
},
{
- "code": "GL"
+ "code": "GL",
+ "numeric": "304"
},
{
- "code": "GD"
+ "code": "GD",
+ "numeric": "308"
},
{
- "code": "GP"
+ "code": "GP",
+ "numeric": "312"
},
{
- "code": "GU"
+ "code": "GU",
+ "numeric": "316"
},
{
- "code": "GT"
+ "code": "GT",
+ "numeric": "320"
},
{
- "code": "GN"
+ "code": "GN",
+ "numeric": "324"
},
{
- "code": "GW"
+ "code": "GW",
+ "numeric": "624"
},
{
- "code": "GY"
+ "code": "GY",
+ "numeric": "328"
},
{
- "code": "HT"
+ "code": "HT",
+ "numeric": "332"
},
{
- "code": "HM"
+ "code": "HM",
+ "numeric": "334"
},
{
- "code": "HN"
+ "code": "HN",
+ "numeric": "340"
},
{
- "code": "HK"
+ "code": "HK",
+ "numeric": "344"
},
{
- "code": "HU"
+ "code": "HU",
+ "numeric": "348"
},
{
- "code": "IS"
+ "code": "IS",
+ "numeric": "352"
},
{
"code": "IN",
+ "numeric": "356",
"states": [
{
"code" : "AN",
@@ -847,6 +947,7 @@
},
{
"code": "ID",
+ "numeric": "360",
"states": [
{
"code" : "AC",
@@ -987,25 +1088,32 @@
]
},
{
- "code": "IR"
+ "code": "IR",
+ "numeric": "364"
},
{
- "code": "IQ"
+ "code": "IQ",
+ "numeric": "368"
},
{
- "code": "IE"
+ "code": "IE",
+ "numeric": "372"
},
{
- "code": "IL"
+ "code": "IL",
+ "numeric": "376"
},
{
- "code": "IT"
+ "code": "IT",
+ "numeric": "380"
},
{
- "code": "JM"
+ "code": "JM",
+ "numeric": "388"
},
{
"code": "JP",
+ "numeric": "392",
"states": [
{
"code" : "23",
@@ -1198,94 +1306,124 @@
]
},
{
- "code": "JO"
+ "code": "JO",
+ "numeric": "400"
},
{
- "code": "KZ"
+ "code": "KZ",
+ "numeric": "398"
},
{
- "code": "KE"
+ "code": "KE",
+ "numeric": "404"
},
{
- "code": "KI"
+ "code": "KI",
+ "numeric": "296"
},
{
- "code": "KP"
+ "code": "KP",
+ "numeric": "408"
},
{
- "code": "KR"
+ "code": "KR",
+ "numeric": "410"
},
{
- "code": "KW"
+ "code": "KW",
+ "numeric": "414"
},
{
- "code": "KG"
+ "code": "KG",
+ "numeric": "417"
},
{
- "code": "LA"
+ "code": "LA",
+ "numeric": "418"
},
{
- "code": "LV"
+ "code": "LV",
+ "numeric": "428"
},
{
- "code": "LB"
+ "code": "LB",
+ "numeric": "422"
},
{
- "code": "LS"
+ "code": "LS",
+ "numeric": "426"
},
{
- "code": "LR"
+ "code": "LR",
+ "numeric": "430"
},
{
- "code": "LY"
+ "code": "LY",
+ "numeric": "434"
},
{
- "code": "LI"
+ "code": "LI",
+ "numeric": "438"
},
{
- "code": "LT"
+ "code": "LT",
+ "numeric": "440"
},
{
- "code": "LU"
+ "code": "LU",
+ "numeric": "442"
},
{
- "code": "MO"
+ "code": "MO",
+ "numeric": "446"
},
{
- "code": "MG"
+ "code": "MG",
+ "numeric": "450"
},
{
- "code": "MW"
+ "code": "MW",
+ "numeric": "454"
},
{
- "code": "MY"
+ "code": "MY",
+ "numeric": "458"
},
{
- "code": "MV"
+ "code": "MV",
+ "numeric": "462"
},
{
- "code": "ML"
+ "code": "ML",
+ "numeric": "466"
},
{
- "code": "MT"
+ "code": "MT",
+ "numeric": "470"
},
{
- "code": "MH"
+ "code": "MH",
+ "numeric": "584"
},
{
- "code": "MQ"
+ "code": "MQ",
+ "numeric": "474"
},
{
- "code": "MR"
+ "code": "MR",
+ "numeric": "478"
},
{
- "code": "MU"
+ "code": "MU",
+ "numeric": "480"
},
{
- "code": "YT"
+ "code": "YT",
+ "numeric": "175"
},
{
"code": "MX",
+ "numeric": "484",
"states": [
{
"code" : "AGU",
@@ -1418,220 +1556,292 @@
]
},
{
- "code": "FM"
+ "code": "FM",
+ "numeric": "583"
},
{
- "code": "MD"
+ "code": "MD",
+ "numeric": "498"
},
{
- "code": "MC"
+ "code": "MC",
+ "numeric": "492"
},
{
- "code": "MN"
+ "code": "MN",
+ "numeric": "496"
},
{
- "code": "MS"
+ "code": "MS",
+ "numeric": "500"
},
{
- "code": "MA"
+ "code": "MA",
+ "numeric": "504"
},
{
- "code": "MZ"
+ "code": "MZ",
+ "numeric": "508"
},
{
- "code": "MM"
+ "code": "MM",
+ "numeric": "104"
},
{
- "code": "NA"
+ "code": "NA",
+ "numeric": "516"
},
{
- "code": "NR"
+ "code": "NR",
+ "numeric": "520"
},
{
- "code": "NP"
+ "code": "NP",
+ "numeric": "524"
},
{
- "code": "NL"
+ "code": "NL",
+ "numeric": "528"
},
{
- "code": "NC"
+ "code": "NC",
+ "numeric": "540"
},
{
- "code": "NZ"
+ "code": "NZ",
+ "numeric": "554"
},
{
- "code": "NI"
+ "code": "NI",
+ "numeric": "558"
},
{
- "code": "NE"
+ "code": "NE",
+ "numeric": "562"
},
{
- "code": "NG"
+ "code": "NG",
+ "numeric": "566"
},
{
- "code": "NU"
+ "code": "NU",
+ "numeric": "570"
},
{
- "code": "NF"
+ "code": "NF",
+ "numeric": "574"
},
{
- "code": "MP"
+ "code": "MP",
+ "numeric": "580"
},
{
- "code": "NO"
+ "code": "NO",
+ "numeric": "578"
},
{
- "code": "OM"
+ "code": "OM",
+ "numeric": "512"
},
{
- "code": "PK"
+ "code": "PK",
+ "numeric": "586"
},
{
- "code": "PW"
+ "code": "PW",
+ "numeric": "585"
},
{
- "code": "PA"
+ "code": "PA",
+ "numeric": "591"
},
{
- "code": "PG"
+ "code": "PG",
+ "numeric": "598"
},
{
- "code": "PY"
+ "code": "PY",
+ "numeric": "600"
},
{
- "code": "PE"
+ "code": "PE",
+ "numeric": "604"
},
{
- "code": "PH"
+ "code": "PH",
+ "numeric": "608"
},
{
- "code": "PN"
+ "code": "PN",
+ "numeric": "612"
},
{
- "code": "PL"
+ "code": "PL",
+ "numeric": "616"
},
{
- "code": "PT"
+ "code": "PT",
+ "numeric": "620"
},
{
- "code": "PR"
+ "code": "PR",
+ "numeric": "630"
},
{
- "code": "QA"
+ "code": "QA",
+ "numeric": "634"
},
{
- "code": "MK"
+ "code": "MK",
+ "numeric": "807"
},
{
- "code": "RE"
+ "code": "RE",
+ "numeric": "638"
},
{
- "code": "RO"
+ "code": "RO",
+ "numeric": "642"
},
{
- "code": "RU"
+ "code": "RU",
+ "numeric": "643"
},
{
- "code": "RW"
+ "code": "RW",
+ "numeric": "646"
},
{
- "code": "KN"
+ "code": "KN",
+ "numeric": "659"
},
{
- "code": "LC"
+ "code": "LC",
+ "numeric": "662"
},
{
- "code": "VC"
+ "code": "VC",
+ "numeric": "670"
},
{
- "code": "WS"
+ "code": "WS",
+ "numeric": "882"
},
{
- "code": "SM"
+ "code": "SM",
+ "numeric": "674"
},
{
- "code": "ST"
+ "code": "ST",
+ "numeric": "678"
},
{
- "code": "SA"
+ "code": "SA",
+ "numeric": "682"
},
{
- "code": "SN"
+ "code": "SN",
+ "numeric": "686"
},
{
- "code": "SC"
+ "code": "SC",
+ "numeric": "690"
},
{
- "code": "SL"
+ "code": "SL",
+ "numeric": "694"
},
{
- "code": "SG"
+ "code": "SG",
+ "numeric": "702"
},
{
- "code": "SK"
+ "code": "SK",
+ "numeric": "703"
},
{
- "code": "SI"
+ "code": "SI",
+ "numeric": "705"
},
{
- "code": "SB"
+ "code": "SB",
+ "numeric": "090"
},
{
- "code": "SO"
+ "code": "SO",
+ "numeric": "706"
},
{
- "code": "SX"
+ "code": "SX",
+ "numeric": "534"
},
{
- "code": "ZA"
+ "code": "ZA",
+ "numeric": "710"
},
{
- "code": "GS"
+ "code": "GS",
+ "numeric": "239"
},
{
- "code": "ES"
+ "code": "ES",
+ "numeric": "724"
},
{
- "code": "LK"
+ "code": "LK",
+ "numeric": "144"
},
{
- "code": "SH"
+ "code": "SH",
+ "numeric": "654"
},
{
- "code": "PM"
+ "code": "PM",
+ "numeric": "666"
},
{
- "code": "SD"
+ "code": "SD",
+ "numeric": "729"
},
{
- "code": "SR"
+ "code": "SR",
+ "numeric": "740"
},
{
- "code": "SJ"
+ "code": "SJ",
+ "numeric": "744"
},
{
- "code": "SZ"
+ "code": "SZ",
+ "numeric": "748"
},
{
- "code": "SE"
+ "code": "SE",
+ "numeric": "752"
},
{
- "code": "CH"
+ "code": "CH",
+ "numeric": "756"
},
{
- "code": "SY"
+ "code": "SY",
+ "numeric": "760"
},
{
- "code": "TW"
+ "code": "TW",
+ "numeric": "158"
},
{
- "code": "TJ"
+ "code": "TJ",
+ "numeric": "762"
},
{
- "code": "TZ"
+ "code": "TZ",
+ "numeric": "834"
},
{
"code": "TH",
+ "numeric": "764",
"states": [
{
"code" : "37",
@@ -1948,46 +2158,60 @@
]
},
{
- "code": "TG"
+ "code": "TG",
+ "numeric": "768"
},
{
- "code": "TK"
+ "code": "TK",
+ "numeric": "772"
},
{
- "code": "TO"
+ "code": "TO",
+ "numeric": "776"
},
{
- "code": "TT"
+ "code": "TT",
+ "numeric": "780"
},
{
- "code": "TN"
+ "code": "TN",
+ "numeric": "788"
},
{
- "code": "TR"
+ "code": "TR",
+ "numeric": "792"
},
{
- "code": "TM"
+ "code": "TM",
+ "numeric": "795"
},
{
- "code": "TC"
+ "code": "TC",
+ "numeric": "796"
},
{
- "code": "TV"
+ "code": "TV",
+ "numeric": "798"
},
{
- "code": "UG"
+ "code": "UG",
+ "numeric": "800"
},
{
- "code": "UA"
+ "code": "UA",
+ "numeric": "804"
},
{
- "code": "AE"
+ "code": "AE",
+ "numeric": "784"
},
{
- "code": "GB"
+ "code": "GB",
+ "numeric": "826"
},
{
"code": "US",
+ "numeric": "840",
"states": [
{
"code" : "AL",
@@ -2220,42 +2444,55 @@
]
},
{
- "code": "UY"
+ "code": "UY",
+ "numeric": "858"
},
{
- "code": "UZ"
+ "code": "UZ",
+ "numeric": "860"
},
{
- "code": "VU"
+ "code": "VU",
+ "numeric": "548"
},
{
- "code": "VA"
+ "code": "VA",
+ "numeric": "336"
},
{
- "code": "VE"
+ "code": "VE",
+ "numeric": "862"
},
{
- "code": "VN"
+ "code": "VN",
+ "numeric": "704"
},
{
- "code": "VG"
+ "code": "VG",
+ "numeric": "092"
},
{
- "code": "VI"
+ "code": "VI",
+ "numeric": "850"
},
{
- "code": "WF"
+ "code": "WF",
+ "numeric": "876"
},
{
- "code": "YE"
+ "code": "YE",
+ "numeric": "887"
},
{
- "code": "CD"
+ "code": "CD",
+ "numeric": "180"
},
{
- "code": "ZM"
+ "code": "ZM",
+ "numeric": "894"
},
{
- "code": "ZW"
+ "code": "ZW",
+ "numeric": "716"
}
]
diff --git a/ui/src/main/java/io/snabble/sdk/ui/SnabbleUI.kt b/ui/src/main/java/io/snabble/sdk/ui/SnabbleUI.kt
index b060d44cca..33273c9390 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/SnabbleUI.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/SnabbleUI.kt
@@ -19,8 +19,8 @@ import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_BARCODE_SEARCH
import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_CHECKOUT
import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_CHECKOUT_DONE
import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_COUPON_DETAILS
-import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_FISERV_INPUT
import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_EXTERNAL_BILLING
+import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_FISERV_INPUT
import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_GIROPAY_INPUT
import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_PAYMENT_CREDENTIALS_LIST
import io.snabble.sdk.ui.SnabbleUI.Event.SHOW_PAYMENT_OPTIONS
@@ -34,13 +34,14 @@ import io.snabble.sdk.ui.cart.deprecated.ShoppingCartActivity
import io.snabble.sdk.ui.checkout.CheckoutActivity
import io.snabble.sdk.ui.coupon.CouponDetailActivity
import io.snabble.sdk.ui.payment.AgeVerificationInputActivity
-import io.snabble.sdk.ui.payment.fiserv.FiservInputActivity
import io.snabble.sdk.ui.payment.GiropayInputActivity
import io.snabble.sdk.ui.payment.PaymentCredentialsListActivity
import io.snabble.sdk.ui.payment.PaymentOptionsActivity
import io.snabble.sdk.ui.payment.PayoneInputActivity
import io.snabble.sdk.ui.payment.ProjectPaymentOptionsActivity
import io.snabble.sdk.ui.payment.SEPACardInputActivity
+import io.snabble.sdk.ui.payment.creditcard.datatrans.ui.DatatransActivity
+import io.snabble.sdk.ui.payment.creditcard.fiserv.FiservInputActivity
import io.snabble.sdk.ui.payment.externalbilling.ExternalBillingActivity
import io.snabble.sdk.ui.payment.payone.sepa.form.PayoneSepaActivity
import io.snabble.sdk.ui.scanner.SelfScanningActivity
@@ -67,6 +68,7 @@ object SnabbleUI {
SHOW_SEPA_CARD_INPUT,
SHOW_PAYONE_SEPA,
SHOW_FISERV_INPUT,
+ SHOW_DATATRANS_INPUT,
SHOW_PAYONE_INPUT,
SHOW_GIROPAY_INPUT,
SHOW_EXTERNAL_BILLING,
@@ -177,6 +179,9 @@ object SnabbleUI {
SHOW_FISERV_INPUT ->
startActivity(context, FiservInputActivity::class.java, args, canGoBack = false)
+ Event.SHOW_DATATRANS_INPUT ->
+ startActivity(context, DatatransActivity::class.java, args, canGoBack = false)
+
SHOW_PAYONE_INPUT -> startActivity(context, PayoneInputActivity::class.java, args, canGoBack = false)
SHOW_GIROPAY_INPUT ->
@@ -201,6 +206,7 @@ object SnabbleUI {
NOT_CHECKED_IN,
EXIT_TOKEN_AVAILABLE,
null -> Unit
+
}
}
}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/Datatrans.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/Datatrans.kt
deleted file mode 100644
index 73d64c2ffd..0000000000
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/Datatrans.kt
+++ /dev/null
@@ -1,199 +0,0 @@
-package io.snabble.sdk.ui.payment
-
-import android.widget.Toast
-import androidx.fragment.app.FragmentActivity
-import androidx.lifecycle.DefaultLifecycleObserver
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import ch.datatrans.payment.api.Transaction
-import ch.datatrans.payment.api.TransactionListener
-import ch.datatrans.payment.api.TransactionRegistry
-import ch.datatrans.payment.api.TransactionSuccess
-import ch.datatrans.payment.exception.TransactionException
-import ch.datatrans.payment.paymentmethods.SavedCard
-import ch.datatrans.payment.paymentmethods.SavedPostFinanceCard
-import io.snabble.sdk.PaymentMethod
-import io.snabble.sdk.Project
-import io.snabble.sdk.Snabble
-import io.snabble.sdk.payment.PaymentCredentials
-import io.snabble.sdk.ui.Keyguard
-import io.snabble.sdk.ui.R
-import io.snabble.sdk.utils.Dispatch
-import io.snabble.sdk.utils.GsonHolder
-import io.snabble.sdk.utils.Logger
-import io.snabble.sdk.utils.SimpleJsonCallback
-import okhttp3.Callback
-import okhttp3.MediaType.Companion.toMediaType
-import okhttp3.Request
-import okhttp3.RequestBody.Companion.toRequestBody
-import java.util.*
-
-object Datatrans {
- data class DatatransTokenizationRequest(
- val paymentMethod: PaymentMethod,
- val language: String = Locale.getDefault().language,
- )
-
- data class DatatransTokenizationResponse(
- val mobileToken: String,
- val isTesting: Boolean?,
- )
-
- @JvmStatic
- fun registerCard(activity: FragmentActivity, project: Project, paymentMethod: PaymentMethod) {
- val descriptor = project.paymentMethodDescriptors.find { it.paymentMethod == paymentMethod }
- if (descriptor == null) {
- project.events.logError("Datatrans Error: No payment descriptor")
- Logger.e("Datatrans error: No payment method descriptor for $paymentMethod")
-
- Dispatch.mainThread {
- showError(activity, paymentMethod)
- }
- return
- }
-
- val url = descriptor.links?.get("tokenization")
- if (url == null) {
- project.events.logError("Datatrans Error: No tokenization url")
- Logger.e("Datatrans error: No tokenization url")
-
- Dispatch.mainThread {
- showError(activity, paymentMethod)
- }
- return
- }
-
- val request: Request = Request.Builder()
- .url(Snabble.absoluteUrl(url.href))
- .post(
- GsonHolder.get().toJson(
- DatatransTokenizationRequest(paymentMethod)
- ).toRequestBody("application/json".toMediaType())
- )
- .build()
-
- project.okHttpClient.newCall(request).enqueue(object :
- SimpleJsonCallback(DatatransTokenizationResponse::class.java), Callback {
- override fun success(response: DatatransTokenizationResponse) {
- startDatatransTransaction(activity, response, paymentMethod, project)
- }
-
- override fun error(t: Throwable?) {
- Dispatch.mainThread {
- showError(activity, paymentMethod)
- }
-
- project.events.logError("Datatrans Tokenization Error: " + t?.message)
- Logger.e("Datatrans Tokenization Error: ${t?.message}")
- }
- })
- }
-
- private fun showError(activity: FragmentActivity, paymentMethod: PaymentMethod) {
- if (!activity.isDestroyed) {
- val err = when (paymentMethod) {
- PaymentMethod.TWINT -> {
- R.string.Snabble_Payment_Twint_error
- }
-
- PaymentMethod.POST_FINANCE_CARD -> {
- R.string.Snabble_Payment_PostFinanceCard_error
- }
-
- else -> {
- R.string.Snabble_Payment_CreditCard_error
- }
- }
-
- Toast.makeText(activity, err, Toast.LENGTH_LONG).show()
- }
- }
-
- private fun startDatatransTransaction(
- activity: FragmentActivity,
- tokenizationResponse: DatatransTokenizationResponse,
- paymentMethod: PaymentMethod,
- project: Project
- ) {
- val transaction = Transaction(tokenizationResponse.mobileToken)
- transaction.listener = object : TransactionListener {
- override fun onTransactionSuccess(result: TransactionSuccess) {
- activity.runOnUiThreadWhenResumed {
- val token = result.savedPaymentMethod
- var month = ""
- var year = ""
-
- when (token) {
- is SavedPostFinanceCard -> {
- token.cardExpiryDate?.let {
- month = it.formattedMonth
- year = it.formattedYear
- }
- }
-
- is SavedCard -> {
- token.cardExpiryDate?.let {
- month = it.formattedMonth
- year = it.formattedYear
- }
- }
- }
-
- if (token != null) {
- Keyguard.unlock(activity, object : Keyguard.Callback {
- override fun success() {
- val store = Snabble.paymentCredentialsStore
- val credentials = PaymentCredentials.fromDatatrans(
- token.alias,
- PaymentCredentials.Brand.fromPaymentMethod(paymentMethod),
- result.savedPaymentMethod?.getDisplayTitle(activity),
- month,
- year,
- project.id,
- )
- store.add(credentials)
- }
-
- override fun error() {
- Toast.makeText(activity, R.string.Snabble_SEPA_encryptionError, Toast.LENGTH_LONG)
- .show()
- }
- })
- } else {
- Toast.makeText(activity, R.string.Snabble_SEPA_encryptionError, Toast.LENGTH_LONG).show()
- }
- }
- }
-
- override fun onTransactionError(exception: TransactionException) {
- activity.runOnUiThreadWhenResumed {
- project.events.logError("Datatrans TransactionException: " + exception.message)
- showError(activity, paymentMethod)
- }
- }
- }
- transaction.options.appCallbackScheme = "snabble"
- transaction.options.isTesting = tokenizationResponse.isTesting ?: false
- transaction.options.useCertificatePinning = true
- TransactionRegistry.startTransaction(activity, transaction)
- }
-}
-
-fun FragmentActivity.runOnUiThreadWhenResumed(task: () -> Unit) {
- Dispatch.mainThread {
- if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) {
- Dispatch.mainThread {
- task()
- }
- } else {
- lifecycle.addObserver(object : DefaultLifecycleObserver {
- override fun onResume(owner: LifecycleOwner) {
- Dispatch.mainThread {
- task()
- }
- lifecycle.removeObserver(this)
- }
- })
- }
- }
-}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt
index 60d42a5ff7..c5044ad4f3 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/PaymentInputViewHelper.kt
@@ -10,8 +10,9 @@ import io.snabble.sdk.Snabble
import io.snabble.sdk.payment.PaymentCredentials
import io.snabble.sdk.ui.R
import io.snabble.sdk.ui.SnabbleUI
+import io.snabble.sdk.ui.payment.creditcard.datatrans.ui.DatatransFragment
+import io.snabble.sdk.ui.payment.creditcard.fiserv.FiservInputView
import io.snabble.sdk.ui.payment.externalbilling.ExternalBillingFragment.Companion.ARG_PROJECT_ID
-import io.snabble.sdk.ui.payment.fiserv.FiservInputView
import io.snabble.sdk.ui.utils.KeyguardUtils
import io.snabble.sdk.ui.utils.UIUtils
import io.snabble.sdk.utils.Logger
@@ -36,13 +37,18 @@ object PaymentInputViewHelper {
val args = Bundle()
when {
- useDatatrans -> Datatrans.registerCard(activity, project, paymentMethod)
+ useDatatrans -> {
+ args.putString(DatatransFragment.ARG_PROJECT_ID, projectId)
+ args.putSerializable(DatatransFragment.ARG_PAYMENT_TYPE, paymentMethod)
+ SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_DATATRANS_INPUT, args)
+ }
usePayone -> Payone.registerCard(activity, project, paymentMethod, Snabble.formPrefillData)
useFiserv -> {
args.putString(FiservInputView.ARG_PROJECT_ID, projectId)
args.putSerializable(FiservInputView.ARG_PAYMENT_TYPE, paymentMethod)
SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_FISERV_INPUT, args)
}
+
paymentMethod == PaymentMethod.EXTERNAL_BILLING -> {
args.putString(ARG_PROJECT_ID, projectId)
SnabbleUI.executeAction(context, SnabbleUI.Event.SHOW_EXTERNAL_BILLING, args)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/DatatransRemoteDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/DatatransRemoteDataSource.kt
new file mode 100644
index 0000000000..0b35034b24
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/DatatransRemoteDataSource.kt
@@ -0,0 +1,44 @@
+package io.snabble.sdk.ui.payment.creditcard.datatrans.data
+
+import com.google.gson.Gson
+import io.snabble.sdk.Snabble
+import io.snabble.sdk.ui.payment.creditcard.datatrans.data.dto.AuthDataDto
+import io.snabble.sdk.ui.payment.creditcard.datatrans.data.dto.CustomerDataDto
+import io.snabble.sdk.ui.payment.creditcard.shared.getTokenizationUrlFor
+import io.snabble.sdk.ui.payment.creditcard.shared.post
+import io.snabble.sdk.utils.GsonHolder
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.Request
+import okhttp3.RequestBody
+import okhttp3.RequestBody.Companion.toRequestBody
+
+internal interface DatatransRemoteDataSource {
+
+ suspend fun sendUserData(customerDataDto: CustomerDataDto): Result
+}
+
+internal class DatatransRemoteDataSourceImpl(
+ private val snabble: Snabble = Snabble,
+ private val gson: Gson = GsonHolder.get(),
+) : DatatransRemoteDataSource {
+
+ override suspend fun sendUserData(
+ customerDataDto: CustomerDataDto,
+ ): Result {
+ val project =
+ snabble.checkedInProject.value ?: return Result.failure(Exception("Missing projectId"))
+
+ val customerInfoPostUrl =
+ project.paymentMethodDescriptors.getTokenizationUrlFor(customerDataDto.paymentMethod)
+ ?: return Result.failure(Exception("Missing link to send customer info to"))
+
+ val requestBody: RequestBody =
+ gson.toJson(customerDataDto).toRequestBody("application/json".toMediaType())
+ val request: Request = Request.Builder()
+ .url(customerInfoPostUrl)
+ .post(requestBody)
+ .build()
+
+ return project.okHttpClient.post(request, gson)
+ }
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/DatatransRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/DatatransRepositoryImpl.kt
new file mode 100644
index 0000000000..c57084858d
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/DatatransRepositoryImpl.kt
@@ -0,0 +1,53 @@
+package io.snabble.sdk.ui.payment.creditcard.datatrans.data
+
+import io.snabble.sdk.PaymentMethod
+import io.snabble.sdk.ui.payment.creditcard.datatrans.data.dto.AddressDto
+import io.snabble.sdk.ui.payment.creditcard.datatrans.data.dto.AuthDataDto
+import io.snabble.sdk.ui.payment.creditcard.datatrans.data.dto.CustomerDataDto
+import io.snabble.sdk.ui.payment.creditcard.datatrans.data.dto.CustomerInfoDto
+import io.snabble.sdk.ui.payment.creditcard.datatrans.data.dto.PhoneNumberDto
+import io.snabble.sdk.ui.payment.creditcard.datatrans.domain.DatatransRepository
+import io.snabble.sdk.ui.payment.creditcard.datatrans.domain.model.AuthData
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CustomerInfo
+import java.util.Locale
+
+internal class DatatransRepositoryImpl(
+ private val datatransRemoteDataSource: DatatransRemoteDataSource = DatatransRemoteDataSourceImpl(),
+) : DatatransRepository {
+
+ override suspend fun sendUserData(
+ customerInfo: CustomerInfo,
+ paymentMethod: PaymentMethod
+ ): Result {
+ return datatransRemoteDataSource.sendUserData(createTokenizationRequest(customerInfo, paymentMethod))
+ .map { it.toResponse() }
+ }
+}
+
+private fun createTokenizationRequest(
+ customerInfo: CustomerInfo,
+ paymentMethod: PaymentMethod
+) = CustomerDataDto(
+ paymentMethod = paymentMethod,
+ language = Locale.getDefault().language,
+ cardOwner = customerInfo.toDto()
+)
+
+private fun CustomerInfo.toDto() = CustomerInfoDto(
+ name = name,
+ email = email,
+ address = AddressDto(
+ street = address.street,
+ city = address.city,
+ country = address.country.numericCode,
+ state = address.state,
+ zip = address.zip
+ ),
+ phoneNumber = PhoneNumberDto(
+ intCallingCode = intCallingCode,
+ number = phoneNumber
+ )
+)
+
+private fun AuthDataDto.toResponse() =
+ AuthData(mobileToken = mobileToken, isTesting = isTesting)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/dto/AuthDataDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/dto/AuthDataDto.kt
new file mode 100644
index 0000000000..4d407f7869
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/dto/AuthDataDto.kt
@@ -0,0 +1,8 @@
+package io.snabble.sdk.ui.payment.creditcard.datatrans.data.dto
+
+import com.google.gson.annotations.SerializedName
+
+internal data class AuthDataDto(
+ @SerializedName("mobileToken") val mobileToken: String,
+ @SerializedName("isTesting") val isTesting: Boolean = false,
+)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/dto/CustomerDataDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/dto/CustomerDataDto.kt
new file mode 100644
index 0000000000..e7dd70e796
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/dto/CustomerDataDto.kt
@@ -0,0 +1,11 @@
+package io.snabble.sdk.ui.payment.creditcard.datatrans.data.dto
+
+import com.google.gson.annotations.SerializedName
+import io.snabble.sdk.PaymentMethod
+import java.util.Locale
+
+internal data class CustomerDataDto(
+ @SerializedName("paymentMethod") val paymentMethod: PaymentMethod,
+ @SerializedName("language") val language: String = Locale.getDefault().language,
+ @SerializedName("cardOwner") val cardOwner: CustomerInfoDto
+)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/dto/CustomerInfoDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/dto/CustomerInfoDto.kt
new file mode 100644
index 0000000000..f8107e3d81
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/data/dto/CustomerInfoDto.kt
@@ -0,0 +1,23 @@
+package io.snabble.sdk.ui.payment.creditcard.datatrans.data.dto
+
+import com.google.gson.annotations.SerializedName
+
+internal data class CustomerInfoDto(
+ @SerializedName("name") val name: String,
+ @SerializedName("email") val email: String,
+ @SerializedName("address") val address: AddressDto,
+ @SerializedName("phoneNumber") val phoneNumber: PhoneNumberDto,
+)
+
+internal data class AddressDto(
+ @SerializedName("street") val street: String,
+ @SerializedName("zip") val zip: String,
+ @SerializedName("city") val city: String,
+ @SerializedName("state") val state: String?,
+ @SerializedName("country") val country: String,
+)
+
+internal data class PhoneNumberDto(
+ @SerializedName("subscriber") val number: String,
+ @SerializedName("countryCode") val intCallingCode: String,
+)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/domain/DatatransRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/domain/DatatransRepository.kt
new file mode 100644
index 0000000000..7b1e0d59c4
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/domain/DatatransRepository.kt
@@ -0,0 +1,13 @@
+package io.snabble.sdk.ui.payment.creditcard.datatrans.domain
+
+import io.snabble.sdk.PaymentMethod
+import io.snabble.sdk.ui.payment.creditcard.datatrans.domain.model.AuthData
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CustomerInfo
+
+internal interface DatatransRepository {
+
+ suspend fun sendUserData(
+ customerInfo: CustomerInfo,
+ paymentMethod: PaymentMethod
+ ): Result
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/domain/model/AuthData.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/domain/model/AuthData.kt
new file mode 100644
index 0000000000..cdd2ddbd0c
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/domain/model/AuthData.kt
@@ -0,0 +1,6 @@
+package io.snabble.sdk.ui.payment.creditcard.datatrans.domain.model
+
+data class AuthData(
+ val mobileToken: String,
+ val isTesting: Boolean,
+)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/ui/DatatransActivity.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/ui/DatatransActivity.kt
new file mode 100644
index 0000000000..1074c6ed0e
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/ui/DatatransActivity.kt
@@ -0,0 +1,16 @@
+package io.snabble.sdk.ui.payment.creditcard.datatrans.ui
+
+import androidx.fragment.app.Fragment
+import io.snabble.sdk.ui.BaseFragmentActivity
+
+class DatatransActivity : BaseFragmentActivity() {
+
+ override fun onCreateFragment(): Fragment = DatatransFragment()
+ .apply { arguments = intent.extras }
+
+ companion object {
+
+ const val ARG_PROJECT_ID = DatatransFragment.ARG_PROJECT_ID
+ const val ARG_PAYMENT_TYPE = DatatransFragment.ARG_PAYMENT_TYPE
+ }
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/ui/DatatransFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/ui/DatatransFragment.kt
new file mode 100644
index 0000000000..ff143f1669
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/ui/DatatransFragment.kt
@@ -0,0 +1,117 @@
+package io.snabble.sdk.ui.payment.creditcard.datatrans.ui
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Toast
+import androidx.appcompat.app.AppCompatActivity
+import androidx.compose.ui.platform.ComposeView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.viewModels
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.lifecycle.flowWithLifecycle
+import androidx.lifecycle.lifecycleScope
+import ch.datatrans.payment.api.TransactionRegistry
+import io.snabble.sdk.PaymentMethod
+import io.snabble.sdk.ui.Keyguard
+import io.snabble.sdk.ui.R
+import io.snabble.sdk.ui.payment.PaymentMethodMetaDataHelper
+import io.snabble.sdk.ui.payment.creditcard.shared.CustomerInfoInputScreen
+import io.snabble.sdk.ui.utils.ThemeWrapper
+import io.snabble.sdk.ui.utils.serializableExtra
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.launch
+
+class DatatransFragment : Fragment() {
+
+ private val viewModel: DatatransViewModel by viewModels { DatatransViewModelFactory(requireContext()) }
+
+ private lateinit var paymentMethod: PaymentMethod
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ paymentMethod = arguments?.serializableExtra(ARG_PAYMENT_TYPE)
+ ?: kotlin.run { activity?.onBackPressed(); return }
+
+ (requireActivity() as? AppCompatActivity)?.supportActionBar?.title =
+ PaymentMethodMetaDataHelper(requireContext()).labelFor(paymentMethod)
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
+ ComposeView(inflater.context).apply {
+ setContent {
+ val uiState: UiState = viewModel.uiState.collectAsStateWithLifecycle().value
+
+ ThemeWrapper {
+ CustomerInfoInputScreen(
+ onErrorProcessed = { viewModel.errorHandled() },
+ isLoading = uiState.isLoading,
+ onSendAction = { viewModel.sendUserData(it) },
+ showError = uiState.showError,
+ countryItems = uiState.countryItems,
+ onBackNavigationClick = { activity?.onBackPressed() }
+ )
+ }
+ }
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ handleEvents()
+ }
+
+ private fun handleEvents() {
+ lifecycleScope.launch {
+ viewModel.event
+ .filterNotNull()
+ .flowWithLifecycle(lifecycle)
+ .collectLatest { event ->
+ when (event) {
+ Event.TransactionFailed -> {
+ val err = when (paymentMethod) {
+ PaymentMethod.TWINT -> R.string.Snabble_Payment_Twint_error
+ PaymentMethod.POST_FINANCE_CARD -> R.string.Snabble_Payment_PostFinanceCard_error
+ else -> R.string.Snabble_Payment_CreditCard_error
+ }
+
+ Toast.makeText(activity, err, Toast.LENGTH_LONG).show()
+ }
+
+ is Event.TransActionCreated -> {
+ activity?.let { TransactionRegistry.startTransaction(it, event.transaction) }
+ }
+
+ is Event.TransActionSucceeded -> activity?.let {
+ Keyguard.unlock(it, object : Keyguard.Callback {
+ override fun success() {
+ viewModel.saveDatatransToken(
+ event.datatransToken,
+ event.datatransToken.token.getDisplayTitle(it)
+ )
+ }
+
+ override fun error() {
+ Toast
+ .makeText(activity, R.string.Snabble_SEPA_encryptionError, Toast.LENGTH_LONG)
+ .show()
+
+ activity?.onBackPressed()
+ }
+ })
+ }
+
+ Event.Finish -> activity?.onBackPressed()
+ }
+ }
+ }
+ }
+
+ companion object {
+
+ const val ARG_PROJECT_ID: String = "projectId"
+ const val ARG_PAYMENT_TYPE: String = "paymentType"
+ }
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/ui/DatatransViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/ui/DatatransViewModel.kt
new file mode 100644
index 0000000000..af1ed348d6
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/ui/DatatransViewModel.kt
@@ -0,0 +1,128 @@
+package io.snabble.sdk.ui.payment.creditcard.datatrans.ui
+
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import ch.datatrans.payment.api.Transaction
+import ch.datatrans.payment.api.TransactionListener
+import ch.datatrans.payment.api.TransactionSuccess
+import ch.datatrans.payment.exception.TransactionException
+import ch.datatrans.payment.paymentmethods.CardExpiryDate
+import ch.datatrans.payment.paymentmethods.SavedCard
+import ch.datatrans.payment.paymentmethods.SavedPaymentMethod
+import ch.datatrans.payment.paymentmethods.SavedPostFinanceCard
+import io.snabble.sdk.PaymentMethod
+import io.snabble.sdk.Snabble
+import io.snabble.sdk.payment.PaymentCredentials
+import io.snabble.sdk.ui.payment.creditcard.datatrans.domain.DatatransRepository
+import io.snabble.sdk.ui.payment.creditcard.datatrans.ui.DatatransFragment.Companion.ARG_PAYMENT_TYPE
+import io.snabble.sdk.ui.payment.creditcard.datatrans.ui.DatatransFragment.Companion.ARG_PROJECT_ID
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.CountryItemsRepository
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CountryItem
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CustomerInfo
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
+
+internal class DatatransViewModel(
+ private val datatransRepository: DatatransRepository,
+ countryItemsRepo: CountryItemsRepository,
+ savedStateHandle: SavedStateHandle
+) : ViewModel() {
+
+ private val _uiState =
+ MutableStateFlow(UiState(countryItems = countryItemsRepo.loadCountryItems()))
+ val uiState: StateFlow = _uiState.asStateFlow()
+
+ private val _event: MutableStateFlow = MutableStateFlow(null)
+ val event = _event.asStateFlow()
+
+ private val paymentMethod = savedStateHandle.get(ARG_PAYMENT_TYPE)
+ private val projectId = savedStateHandle.get(ARG_PROJECT_ID)
+
+ fun sendUserData(customerInfo: CustomerInfo) {
+ viewModelScope.launch {
+ paymentMethod ?: return@launch
+ datatransRepository.sendUserData(customerInfo, paymentMethod)
+ .onSuccess { info ->
+ _uiState.update { it.copy(isLoading = false) }
+ _event.update { Event.TransActionCreated(createTransaction(info.mobileToken, info.isTesting)) }
+ }
+ .onFailure {
+ _uiState.update { it.copy(isLoading = false, showError = true) }
+ }
+ }
+ }
+
+ private fun createTransaction(token: String, isTesting: Boolean) = Transaction(token).apply {
+ listener = object : TransactionListener {
+ override fun onTransactionSuccess(result: TransactionSuccess) {
+ val datatransToken = when (val savedPaymentMethod = result.savedPaymentMethod) {
+ is SavedPostFinanceCard -> DatatransToken(savedPaymentMethod, savedPaymentMethod.cardExpiryDate)
+
+ is SavedCard -> DatatransToken(savedPaymentMethod, savedPaymentMethod.cardExpiryDate)
+
+ else -> {
+ if (savedPaymentMethod != null) DatatransToken(savedPaymentMethod)
+ null
+ }
+ }
+ when {
+ datatransToken != null -> _event.update {
+ Event.TransActionSucceeded(datatransToken)
+ }
+
+ else -> _event.update { Event.TransactionFailed }
+ }
+ }
+
+ override fun onTransactionError(exception: TransactionException) {
+ _event.update { Event.TransactionFailed }
+ }
+ }
+ options.appCallbackScheme = "snabble"
+ options.isTesting = isTesting
+ options.useCertificatePinning = true
+ }
+
+ fun errorHandled() {
+ _uiState.update { it.copy(showError = false) }
+ }
+
+ fun saveDatatransToken(datatransToken: DatatransToken, displayName: String) {
+ val credentials = PaymentCredentials.fromDatatrans(
+ datatransToken.token.alias,
+ PaymentCredentials.Brand.fromPaymentMethod(paymentMethod),
+ displayName,
+ datatransToken.expiryDate?.formattedMonth.orEmpty(),
+ datatransToken.expiryDate?.formattedYear.orEmpty(),
+ projectId,
+ )
+ Snabble.paymentCredentialsStore.add(credentials)
+ _event.update { Event.Finish }
+ }
+}
+
+internal data class UiState(
+ val isLoading: Boolean = false,
+ val countryItems: List,
+ val mobileToken: String? = null,
+ val showError: Boolean = false,
+ val isTesting: Boolean = false,
+ val transaction: Transaction? = null,
+ val datatransToken: DatatransToken? = null
+)
+
+internal sealed interface Event {
+ data object TransactionFailed : Event
+ data class TransActionCreated(val transaction: Transaction) : Event
+ data class TransActionSucceeded(val datatransToken: DatatransToken) : Event
+ data object Finish : Event
+}
+
+internal data class DatatransToken(
+ val token: SavedPaymentMethod,
+ val expiryDate: CardExpiryDate? = null
+)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/ui/DatatransViewModelFactory.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/ui/DatatransViewModelFactory.kt
new file mode 100644
index 0000000000..d5ba9b86e8
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/datatrans/ui/DatatransViewModelFactory.kt
@@ -0,0 +1,33 @@
+package io.snabble.sdk.ui.payment.creditcard.datatrans.ui
+
+import android.content.Context
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.createSavedStateHandle
+import androidx.lifecycle.viewmodel.CreationExtras
+import io.snabble.sdk.ui.payment.creditcard.datatrans.data.DatatransRepositoryImpl
+import io.snabble.sdk.ui.payment.creditcard.shared.country.data.CountryItemsRepositoryImpl
+import io.snabble.sdk.ui.payment.creditcard.shared.country.data.source.LocalCountryItemsDataSourceImpl
+import io.snabble.sdk.utils.GsonHolder
+
+internal class DatatransViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
+
+ override fun create(modelClass: Class, extras: CreationExtras): T {
+ if (!modelClass.isAssignableFrom(DatatransViewModel::class.java)) {
+ throw IllegalArgumentException("Unable to construct viewmodel")
+ }
+
+ val savedStateHandle = extras.createSavedStateHandle()
+ @Suppress("UNCHECKED_CAST")
+ return DatatransViewModel(
+ datatransRepository = DatatransRepositoryImpl(),
+ countryItemsRepo = CountryItemsRepositoryImpl(
+ localCountryItemsDataSource = LocalCountryItemsDataSourceImpl(
+ assetManager = context.assets,
+ gson = GsonHolder.get()
+ )
+ ),
+ savedStateHandle = savedStateHandle
+ ) as T
+ }
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputActivity.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservInputActivity.kt
similarity index 89%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputActivity.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservInputActivity.kt
index c9f8a177e5..620244b0ca 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputActivity.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservInputActivity.kt
@@ -1,4 +1,4 @@
-package io.snabble.sdk.ui.payment.fiserv
+package io.snabble.sdk.ui.payment.creditcard.fiserv
import androidx.fragment.app.Fragment
import io.snabble.sdk.ui.BaseFragmentActivity
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputFragment.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservInputFragment.kt
similarity index 89%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputFragment.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservInputFragment.kt
index 62f44d05b7..18b122c1a1 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputFragment.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservInputFragment.kt
@@ -1,4 +1,4 @@
-package io.snabble.sdk.ui.payment.fiserv
+package io.snabble.sdk.ui.payment.creditcard.fiserv
import android.os.Bundle
import android.view.LayoutInflater
@@ -13,6 +13,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import io.snabble.sdk.PaymentMethod
import io.snabble.sdk.Snabble
import io.snabble.sdk.ui.payment.PaymentMethodMetaDataHelper
+import io.snabble.sdk.ui.payment.creditcard.shared.CustomerInfoInputScreen
import io.snabble.sdk.ui.utils.ThemeWrapper
import io.snabble.sdk.ui.utils.serializableExtra
@@ -25,8 +26,9 @@ class FiservInputFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- paymentMethod = arguments?.serializableExtra(FiservInputView.ARG_PAYMENT_TYPE)
- ?: kotlin.run { activity?.onBackPressed(); return }
+ paymentMethod =
+ arguments?.serializableExtra(FiservInputView.ARG_PAYMENT_TYPE)
+ ?: kotlin.run { activity?.onBackPressed(); return }
(requireActivity() as? AppCompatActivity)?.supportActionBar?.title =
PaymentMethodMetaDataHelper(requireContext()).labelFor(paymentMethod)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputView.java b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservInputView.java
similarity index 98%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputView.java
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservInputView.java
index 87629bab09..49c172c1a7 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservInputView.java
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservInputView.java
@@ -1,4 +1,4 @@
-package io.snabble.sdk.ui.payment.fiserv;
+package io.snabble.sdk.ui.payment.creditcard.fiserv;
import android.annotation.SuppressLint;
import android.app.Activity;
@@ -36,8 +36,8 @@
import io.snabble.sdk.ui.Keyguard;
import io.snabble.sdk.ui.R;
import io.snabble.sdk.ui.SnabbleUI;
-import io.snabble.sdk.ui.payment.creditcard.data.CreditCardInfo;
-import io.snabble.sdk.ui.payment.creditcard.data.SnabbleCreditCardUrlCreator;
+import io.snabble.sdk.ui.payment.creditcard.fiserv.data.CreditCardInfo;
+import io.snabble.sdk.ui.payment.creditcard.fiserv.data.SnabbleCreditCardUrlCreator;
import io.snabble.sdk.ui.telemetry.Telemetry;
import io.snabble.sdk.ui.utils.UIUtils;
import io.snabble.sdk.utils.Dispatch;
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModel.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservViewModel.kt
similarity index 82%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModel.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservViewModel.kt
index 39ff761eeb..82afbb8075 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModel.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservViewModel.kt
@@ -1,13 +1,13 @@
-package io.snabble.sdk.ui.payment.fiserv
+package io.snabble.sdk.ui.payment.creditcard.fiserv
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.snabble.sdk.PaymentMethod
-import io.snabble.sdk.ui.payment.fiserv.domain.CountryItemsRepository
-import io.snabble.sdk.ui.payment.fiserv.domain.CustomerInfo
-import io.snabble.sdk.ui.payment.fiserv.domain.FiservRepository
-import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem
+import io.snabble.sdk.ui.payment.creditcard.fiserv.domain.FiservRepository
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.CountryItemsRepository
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CountryItem
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CustomerInfo
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModelFactory.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservViewModelFactory.kt
similarity index 70%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModelFactory.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservViewModelFactory.kt
index d43bc4e93c..29524c6594 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/FiservViewModelFactory.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/FiservViewModelFactory.kt
@@ -1,16 +1,16 @@
-package io.snabble.sdk.ui.payment.fiserv
+package io.snabble.sdk.ui.payment.creditcard.fiserv
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.CreationExtras
-import io.snabble.sdk.ui.payment.fiserv.data.CountryItemsRepositoryImpl
-import io.snabble.sdk.ui.payment.fiserv.data.FiservRepositoryImpl
-import io.snabble.sdk.ui.payment.fiserv.data.country.LocalCountryItemsDataSourceImpl
+import io.snabble.sdk.ui.payment.creditcard.fiserv.data.FiservRepositoryImpl
+import io.snabble.sdk.ui.payment.creditcard.shared.country.data.CountryItemsRepositoryImpl
+import io.snabble.sdk.ui.payment.creditcard.shared.country.data.source.LocalCountryItemsDataSourceImpl
import io.snabble.sdk.utils.GsonHolder
-class FiservViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
+internal class FiservViewModelFactory(private val context: Context) : ViewModelProvider.Factory {
override fun create(modelClass: Class, extras: CreationExtras): T {
if (!modelClass.isAssignableFrom(FiservViewModel::class.java)) {
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardInfo.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/CreditCardInfo.kt
similarity index 90%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardInfo.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/CreditCardInfo.kt
index 20e7292884..573891bd0f 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardInfo.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/CreditCardInfo.kt
@@ -1,8 +1,7 @@
-package io.snabble.sdk.ui.payment.creditcard.data
+package io.snabble.sdk.ui.payment.creditcard.fiserv.data
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
-import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
@Serializable
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/CreditCardUrlBuilder.kt
similarity index 94%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/CreditCardUrlBuilder.kt
index 2033b74c20..d95beaaf82 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/data/CreditCardUrlBuilder.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/CreditCardUrlBuilder.kt
@@ -1,5 +1,6 @@
@file:JvmName("SnabbleCreditCardUrlCreator")
-package io.snabble.sdk.ui.payment.creditcard.data
+
+package io.snabble.sdk.ui.payment.creditcard.fiserv.data
import android.net.Uri
import io.snabble.sdk.PaymentMethod
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/FiservRemoteDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/FiservRemoteDataSource.kt
new file mode 100644
index 0000000000..527a9e4b8d
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/FiservRemoteDataSource.kt
@@ -0,0 +1,49 @@
+package io.snabble.sdk.ui.payment.creditcard.fiserv.data
+
+import com.google.gson.Gson
+import io.snabble.sdk.PaymentMethod
+import io.snabble.sdk.Snabble
+import io.snabble.sdk.ui.payment.creditcard.fiserv.data.dto.AuthDataDto
+import io.snabble.sdk.ui.payment.creditcard.fiserv.data.dto.CustomerInfoDto
+import io.snabble.sdk.ui.payment.creditcard.shared.getTokenizationUrlFor
+import io.snabble.sdk.ui.payment.creditcard.shared.post
+import io.snabble.sdk.utils.GsonHolder
+import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.Request
+import okhttp3.RequestBody
+import okhttp3.RequestBody.Companion.toRequestBody
+
+internal interface FiservRemoteDataSource {
+
+ suspend fun sendUserData(
+ customerInfo: CustomerInfoDto,
+ paymentMethod: PaymentMethod
+ ): Result
+}
+
+internal class FiservRemoteDataSourceImpl(
+ private val snabble: Snabble = Snabble,
+ private val gson: Gson = GsonHolder.get(),
+) : FiservRemoteDataSource {
+
+ override suspend fun sendUserData(
+ customerInfo: CustomerInfoDto,
+ paymentMethod: PaymentMethod
+ ): Result {
+ val project =
+ snabble.checkedInProject.value ?: return Result.failure(Exception("Missing projectId"))
+
+ val customerInfoPostUrl =
+ project.paymentMethodDescriptors.getTokenizationUrlFor(paymentMethod)
+ ?: return Result.failure(Exception("Missing link to send customer info to"))
+
+ val requestBody: RequestBody =
+ gson.toJson(customerInfo).toRequestBody("application/json".toMediaType())
+ val request: Request = Request.Builder()
+ .url(customerInfoPostUrl)
+ .post(requestBody)
+ .build()
+
+ return project.okHttpClient.post(request, gson)
+ }
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/FiservRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/FiservRepositoryImpl.kt
similarity index 50%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/FiservRepositoryImpl.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/FiservRepositoryImpl.kt
index ea5339e0fc..2d4cba6208 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/FiservRepositoryImpl.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/FiservRepositoryImpl.kt
@@ -1,8 +1,11 @@
-package io.snabble.sdk.ui.payment.fiserv.data
+package io.snabble.sdk.ui.payment.creditcard.fiserv.data
import io.snabble.sdk.PaymentMethod
-import io.snabble.sdk.ui.payment.fiserv.domain.CustomerInfo
-import io.snabble.sdk.ui.payment.fiserv.domain.FiservRepository
+import io.snabble.sdk.ui.payment.creditcard.fiserv.data.dto.AddressDto
+import io.snabble.sdk.ui.payment.creditcard.fiserv.data.dto.CustomerInfoDto
+import io.snabble.sdk.ui.payment.creditcard.fiserv.domain.FiservRepository
+import io.snabble.sdk.ui.payment.creditcard.fiserv.domain.model.AuthData
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CustomerInfo
internal class FiservRepositoryImpl(
private val remoteDataSource: FiservRemoteDataSource = FiservRemoteDataSourceImpl()
@@ -11,26 +14,21 @@ internal class FiservRepositoryImpl(
override suspend fun sendUserData(
customerInfo: CustomerInfo,
paymentMethod: PaymentMethod
- ): Result =
+ ): Result =
remoteDataSource
.sendUserData(customerInfo.toDto(), paymentMethod)
- .map { FiservCardRegisterUrls(it.links.formUrl.href, it.links.deleteUrl.href) }
+ .map { AuthData(it.links.formUrl.href, it.links.deleteUrl.href) }
}
private fun CustomerInfo.toDto() = CustomerInfoDto(
name = name,
- phoneNumber = phoneNumber,
+ phoneNumber = "$intCallingCode$phoneNumber",
email = email,
address = AddressDto(
street = address.street,
zip = address.zip,
city = address.city,
state = address.state.ifEmpty { null },
- country = address.country
+ country = address.country.code
),
)
-
-data class FiservCardRegisterUrls(
- val formUrl: String,
- val preAuthDeleteUrl: String
-)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/dto/AuthDataDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/dto/AuthDataDto.kt
new file mode 100644
index 0000000000..c5110407d3
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/dto/AuthDataDto.kt
@@ -0,0 +1,16 @@
+package io.snabble.sdk.ui.payment.creditcard.fiserv.data.dto
+
+import com.google.gson.annotations.SerializedName
+
+internal data class AuthDataDto(
+ @SerializedName("links") val links: LinksDto
+)
+
+internal data class LinksDto(
+ @SerializedName("self") val deleteUrl: LinkDto,
+ @SerializedName("tokenizationForm") val formUrl: LinkDto
+)
+
+internal data class LinkDto(
+ @SerializedName("href") val href: String
+)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/dto/CustomerInfoDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/dto/CustomerInfoDto.kt
new file mode 100644
index 0000000000..a911cedc14
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/data/dto/CustomerInfoDto.kt
@@ -0,0 +1,18 @@
+package io.snabble.sdk.ui.payment.creditcard.fiserv.data.dto
+
+import com.google.gson.annotations.SerializedName
+
+internal data class CustomerInfoDto(
+ @SerializedName("name") val name: String,
+ @SerializedName("phoneNumber") val phoneNumber: String,
+ @SerializedName("email") val email: String,
+ @SerializedName("address") val address: AddressDto,
+)
+
+internal data class AddressDto(
+ @SerializedName("street") val street: String,
+ @SerializedName("zip") val zip: String,
+ @SerializedName("city") val city: String,
+ @SerializedName("state") val state: String?,
+ @SerializedName("country") val country: String
+)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/domain/FiservRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/domain/FiservRepository.kt
new file mode 100644
index 0000000000..c8da727488
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/domain/FiservRepository.kt
@@ -0,0 +1,13 @@
+package io.snabble.sdk.ui.payment.creditcard.fiserv.domain
+
+import io.snabble.sdk.PaymentMethod
+import io.snabble.sdk.ui.payment.creditcard.fiserv.domain.model.AuthData
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CustomerInfo
+
+internal interface FiservRepository {
+
+ suspend fun sendUserData(
+ customerInfo: CustomerInfo,
+ paymentMethod: PaymentMethod
+ ): Result
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/domain/model/AuthData.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/domain/model/AuthData.kt
new file mode 100644
index 0000000000..fadbbe78a5
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/fiserv/domain/model/AuthData.kt
@@ -0,0 +1,6 @@
+package io.snabble.sdk.ui.payment.creditcard.fiserv.domain.model
+
+data class AuthData(
+ val formUrl: String,
+ val preAuthDeleteUrl: String
+)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/CustomerInfoInputScreen.kt
similarity index 74%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/CustomerInfoInputScreen.kt
index 6757a2147e..9045bc2c23 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/CustomerInfoInputScreen.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/CustomerInfoInputScreen.kt
@@ -1,4 +1,4 @@
-package io.snabble.sdk.ui.payment.fiserv
+package io.snabble.sdk.ui.payment.creditcard.shared
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Arrangement
@@ -23,15 +23,17 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardCapitalization
-import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import io.snabble.sdk.ui.R
import io.snabble.sdk.ui.cart.shoppingcart.utils.rememberTextFieldManager
-import io.snabble.sdk.ui.payment.fiserv.domain.Address
-import io.snabble.sdk.ui.payment.fiserv.domain.CustomerInfo
-import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem
-import io.snabble.sdk.ui.payment.fiserv.widget.CountrySelectionMenu
-import io.snabble.sdk.ui.payment.fiserv.widget.TextInput
+import io.snabble.sdk.ui.payment.creditcard.shared.country.displayName
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.Address
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CountryItem
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CustomerInfo
+import io.snabble.sdk.ui.payment.creditcard.shared.country.ui.CountrySelectionMenu
+import io.snabble.sdk.ui.payment.creditcard.shared.widget.PhoneNumberInput
+import io.snabble.sdk.ui.payment.creditcard.shared.widget.TextInput
+import java.util.Locale
@Composable
internal fun CustomerInfoInputScreen(
@@ -39,28 +41,38 @@ internal fun CustomerInfoInputScreen(
onErrorProcessed: () -> Unit,
showError: Boolean,
isLoading: Boolean,
- countryItems: List?,
+ countryItems: List,
onBackNavigationClick: () -> Unit,
) {
var name by remember { mutableStateOf("") }
+ var intCallingCode by remember { mutableStateOf("") }
var phoneNumber by remember { mutableStateOf("") }
var email by remember { mutableStateOf("") }
var street by remember { mutableStateOf("") }
var zip by remember { mutableStateOf("") }
var city by remember { mutableStateOf("") }
var state by remember { mutableStateOf("") }
- var country by remember { mutableStateOf("") }
+ var country: CountryItem by remember { mutableStateOf(countryItems.loadDefaultCountry()) }
val textFieldManager = rememberTextFieldManager()
val isRequiredStateSet =
- if (!countryItems?.firstOrNull { it.code == country }?.stateItems.isNullOrEmpty()) state.isNotEmpty() else true
- val areRequiredFieldsSet =
- listOf(name, phoneNumber, email, street, zip, city, country).all { it.isNotEmpty() } && isRequiredStateSet
+ if (!countryItems.firstOrNull { it.code == country.code }?.stateItems.isNullOrEmpty()) state.isNotEmpty() else true
+ val areRequiredFieldsSet = listOf(
+ name,
+ intCallingCode,
+ phoneNumber,
+ email,
+ street,
+ zip,
+ city,
+ country.code
+ ).all { it.isNotEmpty() } && isRequiredStateSet
val createCustomerInfo: () -> CustomerInfo = {
CustomerInfo(
name = name,
+ intCallingCode = intCallingCode,
phoneNumber = phoneNumber,
email = email,
address = Address(
@@ -89,21 +101,13 @@ internal fun CustomerInfoInputScreen(
onNext = { textFieldManager.moveFocusToNext() }
)
)
- TextInput(
- modifier = Modifier.fillMaxWidth(),
- value = phoneNumber,
- onValueChanged = {
- phoneNumber = it
- if (showError) onErrorProcessed()
- },
- label = stringResource(R.string.Snabble_Payment_CustomerInfo_phoneNumber),
- keyboardActions = KeyboardActions(
- onNext = { textFieldManager.moveFocusToNext() }
- ),
- keyboardOptions = KeyboardOptions(
- keyboardType = KeyboardType.Phone,
- imeAction = ImeAction.Next
- )
+ PhoneNumberInput(
+ callingCode = intCallingCode,
+ onCallingCodeChanged = { callingCode -> intCallingCode = callingCode },
+ phoneNumber = phoneNumber,
+ onPhoneNumberChanged = { number -> phoneNumber = number },
+ onKeyboardAction = { textFieldManager.moveFocusToNext() },
+ onErrorProcessed = onErrorProcessed,
)
TextInput(
modifier = Modifier.fillMaxWidth(),
@@ -163,8 +167,8 @@ internal fun CustomerInfoInputScreen(
countryItems = countryItems,
selectedCountryCode = country,
selectedStateCode = null,
- onCountrySelected = { (_, countryCode), stateItem ->
- country = countryCode
+ onCountrySelected = { countryItem, stateItem ->
+ country = countryItem
state = stateItem?.code.orEmpty()
if (showError) onErrorProcessed()
}
@@ -198,3 +202,13 @@ internal fun CustomerInfoInputScreen(
}
}
}
+
+private fun List?.loadDefaultCountry(): CountryItem =
+ this?.firstOrNull { it.displayName == Locale.getDefault().country.displayName }
+ ?: this?.firstOrNull { it.code == Locale.GERMANY.displayCountry }
+ ?: CountryItem(
+ displayName = Locale.GERMANY.country.displayName,
+ code = Locale.GERMANY.country,
+ numericCode = "",
+ stateItems = null
+ )
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/Extensions.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/Extensions.kt
new file mode 100644
index 0000000000..4061601d52
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/Extensions.kt
@@ -0,0 +1,60 @@
+package io.snabble.sdk.ui.payment.creditcard.shared
+
+import android.util.Log
+import com.google.gson.Gson
+import com.google.gson.JsonSyntaxException
+import com.google.gson.reflect.TypeToken
+import io.snabble.sdk.PaymentMethod
+import io.snabble.sdk.PaymentMethodDescriptor
+import io.snabble.sdk.Snabble
+import okhttp3.Call
+import okhttp3.Callback
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.Response
+import java.io.IOException
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+
+internal fun List.getTokenizationUrlFor(paymentMethod: PaymentMethod): String? =
+ firstOrNull { it.paymentMethod == paymentMethod }
+ ?.links
+ ?.get("tokenization")
+ ?.href
+ ?.let(Snabble::absoluteUrl)
+
+internal suspend inline fun OkHttpClient.post(request: Request, gson: Gson) = suspendCoroutine> {
+ newCall(request).enqueue(object : Callback {
+
+ override fun onResponse(call: Call, response: Response) {
+ when {
+ response.isSuccessful -> {
+ val body = response.body?.string()
+ val typeToken = object : TypeToken() {}.type
+ val data: T? = try {
+ gson.fromJson(body, typeToken)
+ } catch (e: JsonSyntaxException) {
+ Log.e("Payment", "Error parsing pre-registration response", e)
+ null
+ }
+
+ val result = if (data == null) {
+ Result.failure(Exception("Missing content"))
+ } else {
+ Result.success(data)
+ }
+ it.resume(result)
+ }
+
+ else -> {
+ response.body?.string()
+ it.resume(Result.failure(Exception(response.message)))
+ }
+ }
+ }
+
+ override fun onFailure(call: Call, e: IOException) {
+ it.resume(Result.failure(e))
+ }
+ })
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/DisplayNameHelper.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/DisplayNameHelper.kt
new file mode 100644
index 0000000000..b93608cf1c
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/DisplayNameHelper.kt
@@ -0,0 +1,6 @@
+package io.snabble.sdk.ui.payment.creditcard.shared.country
+
+import java.util.Locale
+
+internal val String.displayName: String
+ get() = Locale("", this).displayName
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/CountryItemsRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/CountryItemsRepositoryImpl.kt
new file mode 100644
index 0000000000..9a82072ccd
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/CountryItemsRepositoryImpl.kt
@@ -0,0 +1,12 @@
+package io.snabble.sdk.ui.payment.creditcard.shared.country.data
+
+import io.snabble.sdk.ui.payment.creditcard.shared.country.data.source.LocalCountryItemsDataSource
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.CountryItemsRepository
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CountryItem
+
+internal class CountryItemsRepositoryImpl(
+ private val localCountryItemsDataSource: LocalCountryItemsDataSource,
+) : CountryItemsRepository {
+
+ override fun loadCountryItems(): List = localCountryItemsDataSource.loadCountries()
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/dto/CountryDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/dto/CountryDto.kt
new file mode 100644
index 0000000000..f5572377fd
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/dto/CountryDto.kt
@@ -0,0 +1,9 @@
+package io.snabble.sdk.ui.payment.creditcard.shared.country.data.dto
+
+import com.google.gson.annotations.SerializedName
+
+internal data class CountryDto(
+ @SerializedName("code") val countryCode: String,
+ @SerializedName("states") val states: List?,
+ @SerializedName("numeric") val numericCode: String,
+)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/dto/country/StateDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/dto/StateDto.kt
similarity index 70%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/dto/country/StateDto.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/dto/StateDto.kt
index 333651b935..0bca4189aa 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/dto/country/StateDto.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/dto/StateDto.kt
@@ -1,4 +1,4 @@
-package io.snabble.sdk.ui.payment.fiserv.data.dto.country
+package io.snabble.sdk.ui.payment.creditcard.shared.country.data.dto
import com.google.gson.annotations.SerializedName
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/source/LocalCountryItemsDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/source/LocalCountryItemsDataSource.kt
new file mode 100644
index 0000000000..4abc40870c
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/source/LocalCountryItemsDataSource.kt
@@ -0,0 +1,8 @@
+package io.snabble.sdk.ui.payment.creditcard.shared.country.data.source
+
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CountryItem
+
+internal interface LocalCountryItemsDataSource {
+
+ fun loadCountries(): List
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/source/LocalCountryItemsDataSourceImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/source/LocalCountryItemsDataSourceImpl.kt
new file mode 100644
index 0000000000..430dc9f693
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/data/source/LocalCountryItemsDataSourceImpl.kt
@@ -0,0 +1,38 @@
+package io.snabble.sdk.ui.payment.creditcard.shared.country.data.source
+
+import android.content.res.AssetManager
+import com.google.gson.Gson
+import com.google.gson.reflect.TypeToken
+import io.snabble.sdk.ui.payment.creditcard.shared.country.data.dto.CountryDto
+import io.snabble.sdk.ui.payment.creditcard.shared.country.displayName
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CountryItem
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.StateItem
+import java.io.InputStreamReader
+
+internal class LocalCountryItemsDataSourceImpl(
+ private val assetManager: AssetManager,
+ private val gson: Gson,
+) : LocalCountryItemsDataSource {
+
+ override fun loadCountries(): List {
+ val jsonFileReader: InputStreamReader =
+ assetManager.open(COUNTRIES_AND_STATES_FILE).reader()
+ val type = object : TypeToken>() {}.type
+ return gson
+ .fromJson>(jsonFileReader, type)
+ .map {
+ CountryItem(
+ displayName = it.countryCode.displayName,
+ code = it.countryCode,
+ numericCode = it.numericCode,
+ stateItems = it.states?.map(StateItem.Companion::from)
+ )
+ }
+ .sortedBy { it.displayName }
+ }
+
+ companion object {
+
+ private const val COUNTRIES_AND_STATES_FILE = "countriesAndStates.json"
+ }
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/domain/CountryItemsRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/domain/CountryItemsRepository.kt
new file mode 100644
index 0000000000..4aa4ad7a10
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/domain/CountryItemsRepository.kt
@@ -0,0 +1,8 @@
+package io.snabble.sdk.ui.payment.creditcard.shared.country.domain
+
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CountryItem
+
+internal interface CountryItemsRepository {
+
+ fun loadCountryItems(): List
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/domain/models/CountryItem.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/domain/models/CountryItem.kt
new file mode 100644
index 0000000000..8ddf8b128a
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/domain/models/CountryItem.kt
@@ -0,0 +1,8 @@
+package io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models
+
+internal data class CountryItem(
+ val displayName: String,
+ val code: String,
+ val numericCode: String,
+ val stateItems: List?
+)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/domain/models/CustomerInfo.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/domain/models/CustomerInfo.kt
new file mode 100644
index 0000000000..b301a6d149
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/domain/models/CustomerInfo.kt
@@ -0,0 +1,17 @@
+package io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models
+
+internal data class CustomerInfo(
+ val name: String,
+ val intCallingCode: String,
+ val phoneNumber: String,
+ val email: String,
+ val address: Address,
+)
+
+internal data class Address(
+ val street: String,
+ val zip: String,
+ val city: String,
+ val state: String,
+ val country: CountryItem
+)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/model/country/StateItem.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/domain/models/StateItem.kt
similarity index 62%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/model/country/StateItem.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/domain/models/StateItem.kt
index ea2fe103dd..15376650ea 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/model/country/StateItem.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/domain/models/StateItem.kt
@@ -1,8 +1,9 @@
-package io.snabble.sdk.ui.payment.fiserv.domain.model.country
+package io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models
-import io.snabble.sdk.ui.payment.fiserv.data.dto.country.StateDto
+import io.snabble.sdk.ui.payment.creditcard.shared.country.data.dto.StateDto
internal data class StateItem(val displayName: String, val code: String) {
+
companion object {
fun from(stateDto: StateDto) = StateItem(
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/CountrySelectionMenu.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/ui/CountrySelectionMenu.kt
similarity index 76%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/CountrySelectionMenu.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/ui/CountrySelectionMenu.kt
index 73146a94c8..a63a569022 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/CountrySelectionMenu.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/ui/CountrySelectionMenu.kt
@@ -1,4 +1,4 @@
-package io.snabble.sdk.ui.payment.fiserv.widget
+package io.snabble.sdk.ui.payment.creditcard.shared.country.ui
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.MaterialTheme
@@ -11,17 +11,15 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.sp
-import io.snabble.sdk.ui.payment.fiserv.domain.model.country.StateItem
import io.snabble.sdk.ui.R
-import io.snabble.sdk.ui.payment.fiserv.data.country.displayName
-import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem
-import java.util.Locale
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.CountryItem
+import io.snabble.sdk.ui.payment.creditcard.shared.country.domain.models.StateItem
@Composable
internal fun CountrySelectionMenu(
modifier: Modifier = Modifier,
- countryItems: List?,
- selectedCountryCode: String? = null,
+ countryItems: List,
+ selectedCountryCode: CountryItem,
selectedStateCode: String? = null,
onCountrySelected: (CountryItem, StateItem?) -> Unit,
) {
@@ -31,13 +29,7 @@ internal fun CountrySelectionMenu(
var showStateList by remember { mutableStateOf(false) }
var dismissStateList by remember { mutableStateOf(true) }
- var currentCountryItem by remember {
- mutableStateOf(
- selectedCountryCode?.let { countryCode -> countryItems?.firstOrNull { it.code == countryCode } }
- ?: countryItems.loadDefaultCountry()
- .also { country -> onCountrySelected(country, null) }
- )
- }
+ var currentCountryItem by remember { mutableStateOf(selectedCountryCode) }
var currentStateItem by remember {
mutableStateOf(
@@ -112,12 +104,3 @@ internal fun CountrySelectionMenu(
}
}
}
-
-private fun List?.loadDefaultCountry(): CountryItem =
- this?.firstOrNull { it.displayName == Locale.getDefault().country.displayName }
- ?: this?.firstOrNull { it.code == Locale.GERMANY.displayCountry }
- ?: CountryItem(
- displayName = Locale.GERMANY.country.displayName,
- code = Locale.GERMANY.country,
- stateItems = null
- )
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/DropDownMenu.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/ui/DropDownMenu.kt
similarity index 98%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/DropDownMenu.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/ui/DropDownMenu.kt
index 25a2c425cc..af9ce8093e 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/DropDownMenu.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/ui/DropDownMenu.kt
@@ -1,4 +1,4 @@
-package io.snabble.sdk.ui.payment.fiserv.widget
+package io.snabble.sdk.ui.payment.creditcard.shared.country.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/ui/OutlinedCountryCodeField.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/ui/OutlinedCountryCodeField.kt
new file mode 100644
index 0000000000..e0da3a8f45
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/country/ui/OutlinedCountryCodeField.kt
@@ -0,0 +1,49 @@
+package io.snabble.sdk.ui.payment.creditcard.shared.country.ui
+
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.waitForUpOrCancellation
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.pointerInput
+import io.snabble.sdk.ui.payment.creditcard.shared.widget.defaultColors
+
+@Composable
+internal fun OutlinedCountryCodeField(
+ modifier: Modifier = Modifier,
+ label: String,
+ countryCode: String,
+ onClick: () -> Unit = {},
+ enabled: Boolean = true,
+ isError: Boolean = false,
+) {
+ OutlinedTextField(
+ modifier = modifier
+ .fillMaxWidth()
+ .pointerInput(key1 = enabled) {
+ if (!enabled) return@pointerInput
+ awaitEachGesture {
+ awaitFirstDown(pass = PointerEventPass.Initial)
+ waitForUpOrCancellation(pass = PointerEventPass.Initial)?.let {
+ onClick()
+ }
+ }
+ },
+ readOnly = true,
+ value = countryCode,
+ onValueChange = { },
+ textStyle = MaterialTheme.typography.bodyLarge,
+ label = { Text(text = label) },
+ maxLines = 1,
+ singleLine = true,
+ enabled = enabled,
+ isError = isError,
+ colors = TextFieldDefaults.defaultColors()
+ )
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/widget/PhoneNumberInput.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/widget/PhoneNumberInput.kt
new file mode 100644
index 0000000000..e07a34e93f
--- /dev/null
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/widget/PhoneNumberInput.kt
@@ -0,0 +1,137 @@
+package io.snabble.sdk.ui.payment.creditcard.shared.widget
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.focus.onFocusChanged
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import io.snabble.countrycodepicker.ui.CountryCodePickerDialog
+import io.snabble.sdk.ui.R
+import io.snabble.sdk.ui.payment.creditcard.shared.country.ui.OutlinedCountryCodeField
+
+@Composable
+internal fun PhoneNumberInput(
+ modifier: Modifier = Modifier,
+ callingCode: String,
+ onCallingCodeChanged: (String) -> Unit,
+ enableCallingCodePicking: Boolean = true,
+ phoneNumber: String,
+ onPhoneNumberChanged: (String) -> Unit,
+ enablePhoneNumberInput: Boolean = true,
+ readOnlyPhoneNumberInput: Boolean = false,
+ keyboardAction: ImeAction = ImeAction.Next,
+ onKeyboardAction: () -> Unit,
+ focusPhoneNumberInput: Boolean = false,
+ errorMessage: String? = null,
+ onErrorProcessed: () -> Unit,
+) {
+ var countryCode by rememberSaveable { mutableStateOf("") }
+ var showCodePicker by rememberSaveable { mutableStateOf(false) }
+
+ Column(
+ modifier = modifier,
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ horizontalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ val phoneNumberFocusRequester = remember { FocusRequester() }
+ CountryCodePickerDialog(
+ initialCallingCode = callingCode,
+ showDialog = showCodePicker,
+ // Called initially, so that the country code should never be empty
+ onCountrySelected = { callingCode, flagEmoji ->
+ countryCode = "$flagEmoji $callingCode"
+ onCallingCodeChanged(callingCode)
+ showCodePicker = false
+ },
+ onDismissRequest = {
+ showCodePicker = false
+ if (phoneNumber.isBlank()) phoneNumberFocusRequester.requestFocus()
+ }
+ )
+
+ val callingCodeFocusRequester = remember { FocusRequester() }
+ if (showCodePicker) callingCodeFocusRequester.requestFocus()
+ OutlinedCountryCodeField(
+ modifier = Modifier
+ .widthIn(max = 120.dp)
+ .focusRequester(callingCodeFocusRequester)
+ .onFocusChanged { if (errorMessage != null) onErrorProcessed() },
+ label = stringResource(id = R.string.Snabble_Payment_CustomerInfo_intCallingCode),
+ countryCode = countryCode,
+ onClick = { showCodePicker = true },
+ enabled = enableCallingCodePicking,
+ isError = errorMessage != null
+ )
+
+ OutlinedTextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .focusRequester(phoneNumberFocusRequester)
+ .onFocusChanged {
+ if (errorMessage != null) onErrorProcessed()
+ },
+ value = phoneNumber,
+ onValueChange = { onPhoneNumberChanged(it) },
+ textStyle = MaterialTheme.typography.bodyLarge,
+ label = {
+ Text(
+ text = stringResource(id = R.string.Snabble_Payment_CustomerInfo_phoneNumber),
+ fontSize = 17.sp,
+ )
+ },
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.Phone,
+ imeAction = keyboardAction
+ ),
+ keyboardActions = KeyboardActions(
+ onSend = {
+ onKeyboardAction()
+ }
+ ),
+ isError = errorMessage != null,
+ maxLines = 1,
+ singleLine = true,
+ enabled = enablePhoneNumberInput,
+ readOnly = readOnlyPhoneNumberInput,
+ colors = TextFieldDefaults.defaultColors()
+ )
+ if (focusPhoneNumberInput) phoneNumberFocusRequester.requestFocus()
+ }
+ if (errorMessage != null) {
+ Text(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 8.dp),
+ text = errorMessage,
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.error
+ )
+ }
+ }
+}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/TextInput.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/widget/TextInput.kt
similarity index 96%
rename from ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/TextInput.kt
rename to ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/widget/TextInput.kt
index 5f5a40ccbc..0de64fa6f3 100644
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/widget/TextInput.kt
+++ b/ui/src/main/java/io/snabble/sdk/ui/payment/creditcard/shared/widget/TextInput.kt
@@ -1,4 +1,4 @@
-package io.snabble.sdk.ui.payment.fiserv.widget
+package io.snabble.sdk.ui.payment.creditcard.shared.widget
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
@@ -64,7 +64,7 @@ internal fun TextInput(
}
@Composable
-private fun TextFieldDefaults.defaultColors() = colors(
+internal fun TextFieldDefaults.defaultColors() = colors(
focusedContainerColor = MaterialTheme.colorScheme.background,
unfocusedContainerColor = MaterialTheme.colorScheme.background,
focusedIndicatorColor = MaterialTheme.colorScheme.primary,
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CountryItemsRepositoryImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CountryItemsRepositoryImpl.kt
deleted file mode 100644
index d0c153bb93..0000000000
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CountryItemsRepositoryImpl.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.snabble.sdk.ui.payment.fiserv.data
-
-import io.snabble.sdk.ui.payment.fiserv.data.country.LocalCountryItemsDataSource
-import io.snabble.sdk.ui.payment.fiserv.domain.CountryItemsRepository
-import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem
-
-internal class CountryItemsRepositoryImpl(
- private val localCountryItemsDataSource: LocalCountryItemsDataSource,
-) : CountryItemsRepository {
-
- override fun loadCountryItems(): List = localCountryItemsDataSource.loadCountries()
-}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CustomerInfoDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CustomerInfoDto.kt
deleted file mode 100644
index 312cd1c2cf..0000000000
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/CustomerInfoDto.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package io.snabble.sdk.ui.payment.fiserv.data
-
-import kotlinx.serialization.SerialName
-import kotlinx.serialization.Serializable
-
-@Serializable
-internal data class CustomerInfoDto(
- @SerialName("name") val name: String,
- @SerialName("phoneNumber") val phoneNumber: String,
- @SerialName("email") val email: String,
- @SerialName("address") val address: AddressDto,
-)
-
-@Serializable
-internal data class AddressDto(
- @SerialName("street") val street: String,
- @SerialName("zip") val zip: String,
- @SerialName("city") val city: String,
- @SerialName("state") val state: String?,
- @SerialName("country") val country: String
-)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/FiservRemoteDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/FiservRemoteDataSource.kt
deleted file mode 100644
index f449ba715c..0000000000
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/FiservRemoteDataSource.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-package io.snabble.sdk.ui.payment.fiserv.data
-
-import android.util.Log
-import com.google.gson.Gson
-import com.google.gson.JsonSyntaxException
-import com.google.gson.annotations.SerializedName
-import io.snabble.sdk.PaymentMethod
-import io.snabble.sdk.Snabble
-import io.snabble.sdk.utils.GsonHolder
-import okhttp3.Call
-import okhttp3.Callback
-import okhttp3.MediaType.Companion.toMediaType
-import okhttp3.OkHttpClient
-import okhttp3.Request
-import okhttp3.RequestBody
-import okhttp3.RequestBody.Companion.toRequestBody
-import okhttp3.Response
-import java.io.IOException
-import kotlin.coroutines.resume
-import kotlin.coroutines.suspendCoroutine
-
-internal interface FiservRemoteDataSource {
-
- suspend fun sendUserData(customerInfo: CustomerInfoDto, paymentMethod: PaymentMethod): Result
-}
-
-internal class FiservRemoteDataSourceImpl(
- private val snabble: Snabble = Snabble,
- private val gson: Gson = GsonHolder.get(),
-) : FiservRemoteDataSource {
-
- override suspend fun sendUserData(
- customerInfo: CustomerInfoDto,
- paymentMethod: PaymentMethod
- ): Result {
- val project = snabble.checkedInProject.value ?: return Result.failure(Exception("Missing projectId"))
-
- val customerInfoPostUrl = project.paymentMethodDescriptors
- .firstOrNull { it.paymentMethod == paymentMethod }
- ?.links
- ?.get("tokenization")
- ?.href
- ?.let(snabble::absoluteUrl)
- ?: return Result.failure(Exception("Missing link to send customer info to"))
-
- val requestBody: RequestBody = gson.toJson(customerInfo).toRequestBody("application/json".toMediaType())
- val request: Request = Request.Builder()
- .url(customerInfoPostUrl)
- .post(requestBody)
- .build()
-
- return project.okHttpClient.post(request)
- }
-
- private suspend fun OkHttpClient.post(request: Request) = suspendCoroutine> {
- newCall(request).enqueue(object : Callback {
-
- override fun onResponse(call: Call, response: Response) {
- when {
- response.isSuccessful -> {
- val body = response.body?.string()
- val creditCardAuthData: CreditCardAuthData? = try {
- gson.fromJson(body, CreditCardAuthData::class.java)
- } catch (e: JsonSyntaxException) {
- Log.e("Fiserv", "Error parsing pre-registration response", e)
- null
- }
-
- val result = if (creditCardAuthData == null) {
- Result.failure(Exception("Missing content"))
- } else {
- Result.success(creditCardAuthData)
- }
- it.resume(result)
- }
-
- else -> it.resume(Result.failure(Exception(response.message)))
- }
- }
-
- override fun onFailure(call: Call, e: IOException) {
- it.resume(Result.failure(e))
- }
- })
- }
-}
-
-internal data class CreditCardAuthData(
- @SerializedName("links") val links: Links
-)
-
-internal data class Links(
- @SerializedName("self") val deleteUrl: Link,
- @SerializedName("tokenizationForm") val formUrl: Link
-)
-
-internal data class Link(
- @SerializedName("href") val href: String
-)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSource.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSource.kt
deleted file mode 100644
index 5f4b7d5eeb..0000000000
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSource.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.snabble.sdk.ui.payment.fiserv.data.country
-
-import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem
-
-internal interface LocalCountryItemsDataSource {
-
- fun loadCountries(): List
-}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSourceImpl.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSourceImpl.kt
deleted file mode 100644
index 53c32201a4..0000000000
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/country/LocalCountryItemsDataSourceImpl.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package io.snabble.sdk.ui.payment.fiserv.data.country
-
-import android.content.res.AssetManager
-import com.google.gson.Gson
-import com.google.gson.reflect.TypeToken
-import io.snabble.sdk.ui.payment.fiserv.domain.model.country.StateItem
-import io.snabble.sdk.ui.payment.fiserv.data.dto.country.CountryDto
-import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem
-import java.util.Locale
-
-internal class LocalCountryItemsDataSourceImpl(
- private val assetManager: AssetManager,
- private val gson: Gson,
-) : LocalCountryItemsDataSource {
-
- override fun loadCountries(): List {
- val typeToken = object : TypeToken>() {}.type
- return gson.fromJson>(
- assetManager.open(COUNTRIES_AND_STATES_FILE).reader(),
- typeToken
- )
- .map { (countryCode, states) ->
- CountryItem(
- displayName = countryCode.displayName,
- code = countryCode,
- stateItems = states?.map { StateItem.from(it) }
- )
- }
- .sortedBy { it.displayName }
- }
-
- companion object {
-
- private const val COUNTRIES_AND_STATES_FILE = "countriesAndStates.json"
- }
-}
-
-val String.displayName: String
- get() = Locale("", this).displayName
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/dto/country/CountryDto.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/dto/country/CountryDto.kt
deleted file mode 100644
index 7cb9c5b703..0000000000
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/data/dto/country/CountryDto.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.snabble.sdk.ui.payment.fiserv.data.dto.country
-
-import com.google.gson.annotations.SerializedName
-
-internal data class CountryDto(
- @SerializedName("code") val countryCode: String,
- @SerializedName("states") val states: List? = null,
-)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CountryItemsRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CountryItemsRepository.kt
deleted file mode 100644
index bb81032f3d..0000000000
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CountryItemsRepository.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.snabble.sdk.ui.payment.fiserv.domain
-
-import io.snabble.sdk.ui.payment.fiserv.domain.model.country.CountryItem
-
-internal interface CountryItemsRepository {
-
- fun loadCountryItems(): List
-}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CustomerInfo.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CustomerInfo.kt
deleted file mode 100644
index 8afc10340e..0000000000
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/CustomerInfo.kt
+++ /dev/null
@@ -1,16 +0,0 @@
-package io.snabble.sdk.ui.payment.fiserv.domain
-
-data class CustomerInfo(
- val name: String,
- val phoneNumber: String,
- val email: String,
- val address: Address,
-)
-
-data class Address(
- val street: String,
- val zip: String,
- val city: String,
- val state: String,
- val country: String
-)
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/FiservRepository.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/FiservRepository.kt
deleted file mode 100644
index 7bd9a5129e..0000000000
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/FiservRepository.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package io.snabble.sdk.ui.payment.fiserv.domain
-
-import io.snabble.sdk.PaymentMethod
-import io.snabble.sdk.ui.payment.fiserv.data.FiservCardRegisterUrls
-
-internal interface FiservRepository {
-
- suspend fun sendUserData(customerInfo: CustomerInfo, paymentMethod: PaymentMethod): Result
-}
diff --git a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/model/country/CountryItem.kt b/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/model/country/CountryItem.kt
deleted file mode 100644
index c29020cb6c..0000000000
--- a/ui/src/main/java/io/snabble/sdk/ui/payment/fiserv/domain/model/country/CountryItem.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package io.snabble.sdk.ui.payment.fiserv.domain.model.country
-
-internal data class CountryItem(val displayName: String, val code: String, val stateItems: List?)
diff --git a/ui/src/main/res/layout/snabble_fragment_cardinput_fiserv.xml b/ui/src/main/res/layout/snabble_fragment_cardinput_fiserv.xml
index e28d2e0d23..55647e6014 100644
--- a/ui/src/main/res/layout/snabble_fragment_cardinput_fiserv.xml
+++ b/ui/src/main/res/layout/snabble_fragment_cardinput_fiserv.xml
@@ -1,5 +1,5 @@
-
diff --git a/ui/src/main/res/values-de/strings.xml b/ui/src/main/res/values-de/strings.xml
index 63495323eb..e1e5cfa5c3 100644
--- a/ui/src/main/res/values-de/strings.xml
+++ b/ui/src/main/res/values-de/strings.xml
@@ -106,6 +106,7 @@
E-Mail-Adresse
Bitte überprüfe deine Eingaben und versuche es erneut
Vor- und Nachname
+ Ländercode
Weiter
Telefonnummer
Staat
diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml
index e085da7df9..1a6803750b 100644
--- a/ui/src/main/res/values/strings.xml
+++ b/ui/src/main/res/values/strings.xml
@@ -106,6 +106,7 @@
Email
Please check your entries and try again
Full name
+ Country code
Continue
Phone number
State