diff --git a/CHANGELOG.md b/CHANGELOG.md index 02177e4a52..c308ba0549 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,32 @@ # Changelog All notable changes to this project will be documented in this file. +## [0.33.0-beta04] + +### Changes +- Mark all kotlin fragments as open + +## [0.33.0-beta03] + +### Changes +- Small bugfixes + +## [0.33.0-beta02] + +### Fixed +- PaymentOptionsView not always updating +- Improved layout of PaymentOptionsView + +## [0.33.0-beta01] + +### Breaking Changes +- Added support for PSD2 credit cards. This requires that existing credit cards are invalidated, which +can be checked using PaymentCredentialsStore.hasRemovedOldCreditCards() +- 2 new views and there respective fragments related to the new credit card flow have been added: PaymentOptionsView and ProjectPaymentOptionsView. +ProjectCredentialsListView should not be used as a Overview anymore, the new recommended entry point is now PaymentOptionsView. +- SnabbleUI.Callback now provides data as a Bundle. This bundle is mostly used for fragment arguments. +- EVENT_PRODUCT_CONFIRMATION_HIDE now provides its ShoppingCart.Item in the Bundle as a Serializable "cartItem". + ## [0.32.11] ### Fixed diff --git a/build.gradle b/build.gradle index ca028c6cc6..fba5de3822 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ buildscript { dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'com.android.tools.build:gradle:4.1.1' + classpath 'com.android.tools.build:gradle:4.1.2' classpath 'gradle.plugin.com.github.jlouns:gradle-cross-platform-exec-plugin:0.5.0' classpath "gradle.plugin.gmazzo:sqlite-plugin:0.2" } @@ -29,7 +29,7 @@ allprojects { } project.ext { - sdkVersion='0.32.11' + sdkVersion='0.33.0-beta04' versionCode=1 compileSdkVersion=30 diff --git a/core/src/main/java/io/snabble/sdk/Brand.kt b/core/src/main/java/io/snabble/sdk/Brand.kt index bbadd6bef6..5ca629d6cf 100644 --- a/core/src/main/java/io/snabble/sdk/Brand.kt +++ b/core/src/main/java/io/snabble/sdk/Brand.kt @@ -3,4 +3,8 @@ package io.snabble.sdk data class Brand( val id: String, val name: String, -) \ No newline at end of file +) : Comparable { + override fun compareTo(other: Brand) = compareValuesBy(this, other, + { it.id }, + ) +} \ No newline at end of file diff --git a/core/src/main/java/io/snabble/sdk/CheckoutApi.java b/core/src/main/java/io/snabble/sdk/CheckoutApi.java index 279c3c8039..bd4543647a 100644 --- a/core/src/main/java/io/snabble/sdk/CheckoutApi.java +++ b/core/src/main/java/io/snabble/sdk/CheckoutApi.java @@ -491,7 +491,7 @@ public void createPaymentProcess(final String id, checkoutProcessRequest.paymentInformation.originType = paymentCredentials.getType().getOriginType(); checkoutProcessRequest.paymentInformation.encryptedOrigin = data; - if (paymentCredentials.getType() == PaymentCredentials.Type.CREDIT_CARD) { + if (paymentCredentials.getType() == PaymentCredentials.Type.CREDIT_CARD_PSD2) { Date date = new Date(paymentCredentials.getValidTo()); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd"); checkoutProcessRequest.paymentInformation.validUntil = simpleDateFormat.format(date); diff --git a/core/src/main/java/io/snabble/sdk/Project.java b/core/src/main/java/io/snabble/sdk/Project.java index ace83ace55..2e72886ef5 100644 --- a/core/src/main/java/io/snabble/sdk/Project.java +++ b/core/src/main/java/io/snabble/sdk/Project.java @@ -354,6 +354,10 @@ public String getProductByCodeUrl() { return urls.get("resolvedProductLookUp"); } + public String getTelecashVaultItemsUrl() { + return urls.get("telecashVaultItems"); + } + public BarcodeFormat[] getSupportedBarcodeFormats() { return supportedBarcodeFormats; } diff --git a/core/src/main/java/io/snabble/sdk/payment/PaymentCredentials.java b/core/src/main/java/io/snabble/sdk/payment/PaymentCredentials.java index cc7e273cea..68734e84be 100644 --- a/core/src/main/java/io/snabble/sdk/payment/PaymentCredentials.java +++ b/core/src/main/java/io/snabble/sdk/payment/PaymentCredentials.java @@ -2,6 +2,8 @@ import android.util.Base64; +import androidx.annotation.Nullable; + import java.io.InputStream; import java.security.cert.CertPath; import java.security.cert.CertPathValidator; @@ -13,7 +15,6 @@ import java.security.cert.X509Certificate; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.MGF1ParameterSpec; -import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; @@ -22,6 +23,7 @@ import javax.crypto.spec.PSource; import io.snabble.sdk.PaymentMethod; +import io.snabble.sdk.Project; import io.snabble.sdk.R; import io.snabble.sdk.Snabble; import io.snabble.sdk.utils.GsonHolder; @@ -30,20 +32,28 @@ public class PaymentCredentials { public enum Type { - SEPA(null), - CREDIT_CARD(null), - PAYDIREKT(null), - TEGUT_EMPLOYEE_CARD("tegutEmployeeID"); + SEPA(null, Collections.singletonList(PaymentMethod.DE_DIRECT_DEBIT)), + // legacy credit card type, not used anymore. + CREDIT_CARD(null, Arrays.asList(PaymentMethod.VISA, PaymentMethod.MASTERCARD, PaymentMethod.AMEX)), + CREDIT_CARD_PSD2(null, Arrays.asList(PaymentMethod.VISA, PaymentMethod.MASTERCARD, PaymentMethod.AMEX)), + PAYDIREKT(null, Collections.singletonList(PaymentMethod.PAYDIREKT)), + TEGUT_EMPLOYEE_CARD("tegutEmployeeID", Collections.singletonList(PaymentMethod.TEGUT_EMPLOYEE_CARD)); private String originType; + private List paymentMethods; - Type(String originType) { + Type(String originType, List paymentMethods) { this.originType = originType; + this.paymentMethods = paymentMethods; } public String getOriginType() { return originType; } + + public List getPaymentMethods() { + return paymentMethods; + } } public enum Brand { @@ -60,6 +70,8 @@ private static class SepaData { private static class CreditCardData { private String hostedDataID; + private String schemeTransactionID; + private String projectID; private String hostedDataStoreID; private String cardType; } @@ -94,6 +106,7 @@ private static class TegutEmployeeCard { private Brand brand; private String appId; private String id; + private String projectId; private Map additionalData; private PaymentCredentials() { @@ -139,12 +152,18 @@ public static PaymentCredentials fromSEPA(String name, String iban) { return pc; } - public static PaymentCredentials fromCreditCardData(String name, Brand brand, String obfuscatedId, - String expirationMonth, String expirationYear, - String hostedDataId, String storeId) { + public static PaymentCredentials fromCreditCardData(String name, + Brand brand, + String projectId, + String obfuscatedId, + String expirationMonth, + String expirationYear, + String hostedDataId, + String schemeTransactionId, + String storeId) { PaymentCredentials pc = new PaymentCredentials(); pc.generateId(); - pc.type = Type.CREDIT_CARD; + pc.type = Type.CREDIT_CARD_PSD2; List certificates = Snabble.getInstance().getPaymentSigningCertificates(); if (certificates.size() == 0) { @@ -159,6 +178,8 @@ public static PaymentCredentials fromCreditCardData(String name, Brand brand, St CreditCardData creditCardData = new CreditCardData(); creditCardData.hostedDataID = hostedDataId; + creditCardData.projectID = projectId; + creditCardData.schemeTransactionID = schemeTransactionId; creditCardData.hostedDataStoreID = storeId; switch (brand) { @@ -183,6 +204,7 @@ public static PaymentCredentials fromCreditCardData(String name, Brand brand, St pc.signature = pc.sha256Signature(certificate); pc.brand = brand; pc.appId = Snabble.getInstance().getConfig().appId; + pc.projectId = projectId; try { SimpleDateFormat simpleDateFormat = new SimpleDateFormat("MM/yyyy"); @@ -245,7 +267,7 @@ public static PaymentCredentials fromPaydirekt(PaydirektAuthorizationData author return pc; } - public static PaymentCredentials fromTegutEmployeeCard(String obfuscatedId, String cardNumber) { + public static PaymentCredentials fromTegutEmployeeCard(String obfuscatedId, String cardNumber, String projectId) { if (cardNumber == null || cardNumber.length() != 19 || (!cardNumber.startsWith("9280001621") && !cardNumber.startsWith("9280001625") @@ -274,6 +296,7 @@ public static PaymentCredentials fromTegutEmployeeCard(String obfuscatedId, Stri pc.signature = pc.sha256Signature(certificate); pc.brand = Brand.UNKNOWN; pc.appId = Snabble.getInstance().getConfig().appId; + pc.projectId = projectId; pc.canBypassKeyStore = true; if (pc.encryptedData == null) { @@ -299,6 +322,11 @@ public Brand getBrand() { return brand; } + @Nullable + public String getProjectId() { + return projectId; + } + public String getAppId() { return appId; } @@ -447,13 +475,17 @@ private String sha256Signature(X509Certificate certificate) { } public boolean validate() { - if (type == Type.CREDIT_CARD) { + if (type == Type.CREDIT_CARD_PSD2) { Date date = new Date(validTo); if (date.getTime() < System.currentTimeMillis()) { return false; } } + if (type == Type.CREDIT_CARD) { + return false; + } + List certificates = Snabble.getInstance().getPaymentSigningCertificates(); for (X509Certificate cert : certificates) { if (sha256Signature(cert).equals(signature)) { @@ -500,7 +532,7 @@ public Map getAdditionalData() { public PaymentMethod getPaymentMethod() { if (getType() == PaymentCredentials.Type.SEPA) { return PaymentMethod.DE_DIRECT_DEBIT; - } else if (type == PaymentCredentials.Type.CREDIT_CARD) { + } else if (type == Type.CREDIT_CARD_PSD2) { switch (getBrand()) { case VISA: return PaymentMethod.VISA; case AMEX: return PaymentMethod.AMEX; diff --git a/core/src/main/java/io/snabble/sdk/payment/PaymentCredentialsStore.java b/core/src/main/java/io/snabble/sdk/payment/PaymentCredentialsStore.java index 4dcf3a16af..fab34581fd 100644 --- a/core/src/main/java/io/snabble/sdk/payment/PaymentCredentialsStore.java +++ b/core/src/main/java/io/snabble/sdk/payment/PaymentCredentialsStore.java @@ -25,6 +25,7 @@ public class PaymentCredentialsStore { private class Data { private List credentialsList; private String id; + private boolean removedOldCreditCards; private boolean isKeyguarded; } @@ -129,6 +130,13 @@ public String id() { return data.id; } + /** + * @return true if old credit cards before v0.33.0 were added and therefore rendered unusable and deleted + */ + public boolean hasRemovedOldCreditCards() { + return data.removedOldCreditCards; + } + public void add(PaymentCredentials credentials) { if (credentials == null) { return; @@ -220,6 +228,11 @@ private void validate() { if (credentials != null) { if (!credentials.validate()) { removals.add(credentials); + + if (credentials.getType() == PaymentCredentials.Type.CREDIT_CARD) { + data.removedOldCreditCards = true; + } + changed = true; } else { // app id's were not stored in old versions, if its not there assume the diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index b9e2eca039..a006646738 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -31,6 +31,8 @@ + + diff --git a/sample/src/main/java/io/snabble/testapp/App.java b/sample/src/main/java/io/snabble/testapp/App.java index 760576d651..654079a373 100644 --- a/sample/src/main/java/io/snabble/testapp/App.java +++ b/sample/src/main/java/io/snabble/testapp/App.java @@ -36,7 +36,6 @@ public void onCreate() { // if you are using a light mode theme, disable night mode resources // this seems like a bug in android AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); - } public void initBlocking() { diff --git a/sample/src/main/java/io/snabble/testapp/BaseActivity.java b/sample/src/main/java/io/snabble/testapp/BaseActivity.java index 151f841c15..8480da4896 100644 --- a/sample/src/main/java/io/snabble/testapp/BaseActivity.java +++ b/sample/src/main/java/io/snabble/testapp/BaseActivity.java @@ -10,11 +10,15 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; +import java.io.Serializable; + import io.snabble.sdk.codes.ScannedCode; import io.snabble.sdk.ui.SnabbleUI; +import io.snabble.sdk.ui.integration.ProjectPaymentOptionsFragment; import io.snabble.sdk.ui.integration.SelfScanningFragment; import io.snabble.sdk.ui.integration.ZebraSupport; import io.snabble.sdk.ui.payment.PaydirektInputView; +import io.snabble.sdk.ui.payment.PaymentOptionsView; import io.snabble.sdk.ui.scanner.ProductResolver; public abstract class BaseActivity extends AppCompatActivity implements SnabbleUI.Callback { @@ -99,13 +103,13 @@ protected void onStop() { public abstract Fragment onCreateFragment(); @Override - public void execute(SnabbleUI.Action action, Object data) { + public void execute(SnabbleUI.Action action, Bundle args) { switch(action) { case GO_BACK: onBackPressed(); break; case SHOW_SCANNER: - showScannerWithCode((String)data); + showScannerWithCode(args); break; case SHOW_SHOPPING_CART: showShoppingCart(); @@ -138,13 +142,19 @@ public void execute(SnabbleUI.Action action, Object data) { showSEPACardInput(); break; case SHOW_CREDIT_CARD_INPUT: - showCreditCardInput(); + showCreditCardInput(args); break; case SHOW_PAYDIREKT_INPUT: showPaydirektInput(); break; case SHOW_PAYMENT_CREDENTIALS_LIST: - showPaymentCredentialsList(); + showPaymentCredentialsList(args); + break; + case SHOW_PAYMENT_OPTIONS: + showPaymentOptions(); + break; + case SHOW_PROJECT_PAYMENT_OPTIONS: + showProjectPaymentOptions(args); break; case SHOW_AGE_VERIFICATION: showAgeVerification(); @@ -205,10 +215,10 @@ public void showPaymentFailure() { startActivity(intent); } - public void showScannerWithCode(String scannableCode) { + public void showScannerWithCode(Bundle args) { Intent intent = new Intent(this, SelfScanningActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - intent.putExtra(SelfScanningFragment.ARG_SHOW_PRODUCT_CODE, scannableCode); + intent.putExtra("args", args); startActivity(intent); } @@ -223,8 +233,9 @@ public void showSEPACardInput() { startActivity(intent); } - public void showCreditCardInput() { + public void showCreditCardInput(Bundle args) { Intent intent = new Intent(this, CreditCardInputActivity.class); + intent.putExtra("args", args); startActivity(intent); } @@ -233,8 +244,20 @@ public void showPaydirektInput() { startActivity(intent); } - public void showPaymentCredentialsList() { + public void showPaymentCredentialsList(Bundle args) { Intent intent = new Intent(this, PaymentCredentialsListActivity.class); + intent.putExtra("args", args); + startActivity(intent); + } + + public void showPaymentOptions() { + Intent intent = new Intent(this, PaymentOptionsActivity.class); + startActivity(intent); + } + + public void showProjectPaymentOptions(Bundle args) { + Intent intent = new Intent(this, ProjectPaymentOptionsActivity.class); + intent.putExtra("args", args); startActivity(intent); } diff --git a/sample/src/main/java/io/snabble/testapp/CreditCardInputActivity.java b/sample/src/main/java/io/snabble/testapp/CreditCardInputActivity.java index 8dbea9dff0..be7a6e0488 100644 --- a/sample/src/main/java/io/snabble/testapp/CreditCardInputActivity.java +++ b/sample/src/main/java/io/snabble/testapp/CreditCardInputActivity.java @@ -7,7 +7,9 @@ public class CreditCardInputActivity extends BaseActivity { @Override public Fragment onCreateFragment() { - return new CreditCardInputFragment(); + CreditCardInputFragment fragment = new CreditCardInputFragment(); + fragment.setArguments(getIntent().getBundleExtra("args")); + return fragment; } } diff --git a/sample/src/main/java/io/snabble/testapp/HomeFragment.java b/sample/src/main/java/io/snabble/testapp/HomeFragment.java index 62ebcc957e..6ff65818be 100644 --- a/sample/src/main/java/io/snabble/testapp/HomeFragment.java +++ b/sample/src/main/java/io/snabble/testapp/HomeFragment.java @@ -65,7 +65,14 @@ public void onClick(View v) { v.findViewById(R.id.show_pm).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - ((BaseActivity)getActivity()).showPaymentCredentialsList(); + ((BaseActivity)getActivity()).showPaymentCredentialsList(null); + } + }); + + v.findViewById(R.id.show_po).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + ((BaseActivity)getActivity()).showPaymentOptions(); } }); diff --git a/sample/src/main/java/io/snabble/testapp/PaymentCredentialsListActivity.java b/sample/src/main/java/io/snabble/testapp/PaymentCredentialsListActivity.java index d10a844d23..9a3e161d4f 100644 --- a/sample/src/main/java/io/snabble/testapp/PaymentCredentialsListActivity.java +++ b/sample/src/main/java/io/snabble/testapp/PaymentCredentialsListActivity.java @@ -7,7 +7,9 @@ public class PaymentCredentialsListActivity extends BaseActivity{ @Override public Fragment onCreateFragment() { - return new PaymentCredentialsListFragment(); + Fragment fragment = new PaymentCredentialsListFragment(); + fragment.setArguments(getIntent().getBundleExtra("args")); + return fragment; } } diff --git a/sample/src/main/java/io/snabble/testapp/PaymentOptionsActivity.java b/sample/src/main/java/io/snabble/testapp/PaymentOptionsActivity.java new file mode 100644 index 0000000000..90786e27c5 --- /dev/null +++ b/sample/src/main/java/io/snabble/testapp/PaymentOptionsActivity.java @@ -0,0 +1,14 @@ +package io.snabble.testapp; + +import androidx.fragment.app.Fragment; + +import io.snabble.sdk.ui.integration.PaymentCredentialsListFragment; +import io.snabble.sdk.ui.integration.PaymentOptionsFragment; + +public class PaymentOptionsActivity extends BaseActivity{ + @Override + public Fragment onCreateFragment() { + return new PaymentOptionsFragment(); + } +} + diff --git a/sample/src/main/java/io/snabble/testapp/ProjectPaymentOptionsActivity.java b/sample/src/main/java/io/snabble/testapp/ProjectPaymentOptionsActivity.java new file mode 100644 index 0000000000..bb5d0840e8 --- /dev/null +++ b/sample/src/main/java/io/snabble/testapp/ProjectPaymentOptionsActivity.java @@ -0,0 +1,14 @@ +package io.snabble.testapp; + +import androidx.fragment.app.Fragment; +import io.snabble.sdk.ui.integration.ProjectPaymentOptionsFragment; + +public class ProjectPaymentOptionsActivity extends BaseActivity { + @Override + public Fragment onCreateFragment() { + ProjectPaymentOptionsFragment fragment = new ProjectPaymentOptionsFragment(); + fragment.setArguments(getIntent().getBundleExtra("args")); + return fragment; + } +} + diff --git a/sample/src/main/java/io/snabble/testapp/SelfScanningActivity.java b/sample/src/main/java/io/snabble/testapp/SelfScanningActivity.java index ffbaa20de6..6091940bca 100644 --- a/sample/src/main/java/io/snabble/testapp/SelfScanningActivity.java +++ b/sample/src/main/java/io/snabble/testapp/SelfScanningActivity.java @@ -1,8 +1,5 @@ package io.snabble.testapp; -import android.content.Intent; -import android.os.Bundle; - import androidx.fragment.app.Fragment; import io.snabble.sdk.ui.integration.SelfScanningFragment; @@ -10,14 +7,8 @@ public class SelfScanningActivity extends BaseActivity { @Override public Fragment onCreateFragment() { - Intent intent = getIntent(); - String scannableCode = intent.getStringExtra(SelfScanningFragment.ARG_SHOW_PRODUCT_CODE); - SelfScanningFragment selfScanningFragment = new SelfScanningFragment(); - Bundle args = new Bundle(); - args.putString(SelfScanningFragment.ARG_SHOW_PRODUCT_CODE, scannableCode); - selfScanningFragment.setArguments(args); - + selfScanningFragment.setArguments(getIntent().getBundleExtra("args")); return selfScanningFragment; } } diff --git a/sample/src/main/res/layout/fragment_home.xml b/sample/src/main/res/layout/fragment_home.xml index 14f9511105..714a68df2a 100644 --- a/sample/src/main/res/layout/fragment_home.xml +++ b/sample/src/main/res/layout/fragment_home.xml @@ -65,6 +65,12 @@ android:id="@+id/show_pm" android:text="Show Payment Methods" /> +