diff --git a/README.md b/README.md index 880baac2cc..9454520cc1 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@ This README provides the usage manual for the SDK itself. For the full documenta To integrate the Adyen SDK into your project, import the **core**, **utils** and **ui** module by adding the following lines to your build.gradle file. ``` -compile 'com.adyen.checkout:core:1.10.0' -compile 'com.adyen.checkout:utils:1.10.0' -compile 'com.adyen.checkout:ui:1.10.0' -compile 'com.adyen.checkout:cardscan:1.10.0' +compile 'com.adyen.checkout:core:1.11.0' +compile 'com.adyen.checkout:utils:1.11.0' +compile 'com.adyen.checkout:ui:1.11.0' +compile 'com.adyen.checkout:cardscan:1.11.0' ``` > For implementing Custom integration, only the **core** module is required. However, you might also want to include the **utils** module to use Adyen's utility methods such as Luhn check, credit card type detection, etc. diff --git a/adyen-androidpay/build.gradle b/adyen-androidpay/build.gradle index 543c82d892..5b2310332c 100644 --- a/adyen-androidpay/build.gradle +++ b/adyen-androidpay/build.gradle @@ -52,6 +52,6 @@ dependencies { debugCompile project(path: ':adyen-core', configuration: 'debug') releaseCompile project(path: ':adyen-core', configuration: 'release') } else { - compile project(':adyen-core') + provided group: 'com.adyen.checkout', name: 'core', version: "${rootProject.versionName}" } } diff --git a/adyen-androidpay/src/androidTest/java/com/adyen/androidpay/ExampleInstrumentedTest.java b/adyen-androidpay/src/androidTest/java/com/adyen/androidpay/ExampleInstrumentedTest.java deleted file mode 100644 index c045149368..0000000000 --- a/adyen-androidpay/src/androidTest/java/com/adyen/androidpay/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.adyen.androidpay; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static junit.framework.Assert.assertEquals; - -/** - * Instrumentation test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.adyen.androidpay.test", appContext.getPackageName()); - } -} diff --git a/adyen-androidpay/src/main/AndroidManifest.xml b/adyen-androidpay/src/main/AndroidManifest.xml index 8b181e809b..fc399fac19 100644 --- a/adyen-androidpay/src/main/AndroidManifest.xml +++ b/adyen-androidpay/src/main/AndroidManifest.xml @@ -11,7 +11,8 @@ android:value="true" /> + android:name=".ui.AndroidPayActivity" + android:theme="@style/AndroidPayActivity"/> diff --git a/adyen-androidpay/src/main/java/com/adyen/androidpay/AndroidPayService.java b/adyen-androidpay/src/main/java/com/adyen/androidpay/AndroidPayService.java index 3bb682c42e..cf37255e0e 100644 --- a/adyen-androidpay/src/main/java/com/adyen/androidpay/AndroidPayService.java +++ b/adyen-androidpay/src/main/java/com/adyen/androidpay/AndroidPayService.java @@ -96,6 +96,7 @@ public void process(@NonNull final Context context, @NonNull final PaymentReques intent.putExtra("amount", paymentRequest.getAmount()); intent.putExtra("publicKey", paymentMethod.getConfiguration().getPublicKey()); intent.putExtra("merchantName", paymentMethod.getConfiguration().getMerchantName()); + intent.putExtra("environment", paymentMethod.getConfiguration().getEnvironment()); context.startActivity(intent); } diff --git a/adyen-androidpay/src/main/java/com/adyen/androidpay/ui/AndroidPayActivity.java b/adyen-androidpay/src/main/java/com/adyen/androidpay/ui/AndroidPayActivity.java index d41722f08f..755ec3a993 100644 --- a/adyen-androidpay/src/main/java/com/adyen/androidpay/ui/AndroidPayActivity.java +++ b/adyen-androidpay/src/main/java/com/adyen/androidpay/ui/AndroidPayActivity.java @@ -1,7 +1,10 @@ package com.adyen.androidpay.ui; import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -33,6 +36,8 @@ import java.util.Locale; import java.util.Map; +import static com.adyen.core.constants.Constants.PaymentRequest.ADYEN_UI_FINALIZE_INTENT; + public class AndroidPayActivity extends FragmentActivity implements GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks { @@ -51,6 +56,13 @@ public class AndroidPayActivity extends FragmentActivity implements GoogleApiCli private String merchantName; private String currency; + private BroadcastReceiver uiFinalizationIntent = new BroadcastReceiver() { + @Override + public void onReceive(final Context context, final Intent intent) { + finish(); + } + }; + @SuppressWarnings("unchecked") @Override protected void onCreate(Bundle savedInstanceState) { @@ -69,12 +81,22 @@ protected void onCreate(Bundle savedInstanceState) { googleApiClient = getGoogleApiClient(); - supportWalletFragment = createWalletFragment(WalletConstants.ENVIRONMENT_TEST, + int environment = WalletConstants.ENVIRONMENT_TEST; + + if (intent.hasExtra("environment") && intent.getExtras().getString("environment") != null + && !intent.getExtras().getString("environment").isEmpty()) { + environment = Integer.parseInt(intent.getExtras().getString("environment")); + } + + supportWalletFragment = createWalletFragment(environment, WalletFragmentStyle.BuyButtonAppearance.ANDROID_PAY_DARK, WalletConstants.THEME_DARK); // add Wallet fragment to the UI getSupportFragmentManager().beginTransaction() .replace(R.id.android_pay_fragment_container, supportWalletFragment) .commit(); + + LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(uiFinalizationIntent, + new IntentFilter(ADYEN_UI_FINALIZE_INTENT)); } @Override diff --git a/adyen-androidpay/src/main/res/layout/activity_android_pay.xml b/adyen-androidpay/src/main/res/layout/activity_android_pay.xml index aab93fae7a..68740b388b 100644 --- a/adyen-androidpay/src/main/res/layout/activity_android_pay.xml +++ b/adyen-androidpay/src/main/res/layout/activity_android_pay.xml @@ -1,12 +1,22 @@ - + - + - \ No newline at end of file + + + + \ No newline at end of file diff --git a/adyen-androidpay/src/main/res/values/styles.xml b/adyen-androidpay/src/main/res/values/styles.xml new file mode 100644 index 0000000000..c3aea8e4f0 --- /dev/null +++ b/adyen-androidpay/src/main/res/values/styles.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/adyen-core/build.gradle b/adyen-core/build.gradle index 545ee857d9..ef9eebc97a 100644 --- a/adyen-core/build.gradle +++ b/adyen-core/build.gradle @@ -52,5 +52,5 @@ dependencies { compile 'io.reactivex.rxjava2:rxjava:2.1.2' compile 'io.reactivex.rxjava2:rxandroid:2.0.1' - compile 'com.adyen.cse:adyen-cse:1.0.3' + compile 'com.adyen.cse:adyen-cse:1.0.4' } diff --git a/adyen-core/src/main/java/com/adyen/core/DeviceTokenGenerator.java b/adyen-core/src/main/java/com/adyen/core/DeviceTokenGenerator.java index 1880da3af9..0839bd06cd 100644 --- a/adyen-core/src/main/java/com/adyen/core/DeviceTokenGenerator.java +++ b/adyen-core/src/main/java/com/adyen/core/DeviceTokenGenerator.java @@ -20,7 +20,7 @@ final class DeviceTokenGenerator { private static final String TAG = DeviceTokenGenerator.class.getSimpleName(); - private static final String DEVICE_FINGER_PRINT_VERSION = "1.0"; + private static final String DEVICE_FINGER_PRINT_VERSION = "1.1"; private static final String SDK_VERSION = BuildConfig.VERSION_NAME; private DeviceTokenGenerator() { @@ -31,7 +31,7 @@ private DeviceTokenGenerator() { * Uses device and SDK information to create token. * @return token */ - static String getToken(final Context context, final PaymentStateHandler paymentStateHandler) { + static String getToken(final Context context, final PaymentStateHandler paymentStateHandler, boolean isQuickIntegration) { final JSONObject deviceInfo = new JSONObject(); try { final String androidId = Settings.Secure.getString(context.getContentResolver(), @@ -44,6 +44,8 @@ static String getToken(final Context context, final PaymentStateHandler paymentS deviceInfo.put("deviceIdentifier", androidId); deviceInfo.put("locale", StringUtils.getLocale(context)); + deviceInfo.put("integration", (isQuickIntegration) ? "quick" : "custom"); + } catch (final JSONException jsonException) { Log.e(TAG, "Token could not be created", jsonException); paymentStateHandler.setPaymentErrorThrowableAndTriggerError(jsonException); diff --git a/adyen-core/src/main/java/com/adyen/core/PaymentStateHandler.java b/adyen-core/src/main/java/com/adyen/core/PaymentStateHandler.java index 0aaf69182f..483dac690c 100644 --- a/adyen-core/src/main/java/com/adyen/core/PaymentStateHandler.java +++ b/adyen-core/src/main/java/com/adyen/core/PaymentStateHandler.java @@ -74,7 +74,7 @@ class PaymentStateHandler implements State.StateChangeListener { private PaymentRequestResult paymentResult; private PaymentRequest paymentRequest; - private List filteredPaymentMethodsList; + private List availablePaymentMethods; private List preferredPaymentMethods; private PaymentProcessorStateMachine paymentProcessorStateMachine; @@ -92,7 +92,7 @@ class PaymentStateHandler implements State.StateChangeListener { this.merchantPaymentRequestDetailsListener = paymentRequestDetailsListener; paymentBroadcastReceivers = new PaymentBroadcastReceivers(this, paymentRequest); - filteredPaymentMethodsList = new ArrayList<>(); + availablePaymentMethods = new ArrayList<>(); paymentProcessorStateMachine = new PaymentProcessorStateMachine(this); paymentRequestListeners.add(paymentRequestListener); @@ -184,7 +184,7 @@ public void onStateNotChanged(State state) { private void requestPaymentData() { Log.d(TAG, "requestPaymentData()"); - final String token = DeviceTokenGenerator.getToken(context, this); + final String token = DeviceTokenGenerator.getToken(context, this, sdkHandlesUI()); for (PaymentRequestListener listener : paymentRequestListeners) { listener.onPaymentDataRequested(paymentRequest, token, paymentBroadcastReceivers.getPaymentDataCallback()); @@ -210,7 +210,7 @@ private void requestPaymentMethodSelection() { for (PaymentRequestDetailsListener detailsListener : paymentRequestDetailsListeners) { detailsListener.onPaymentMethodSelectionRequired(paymentRequest, preferredPaymentMethods, - filteredPaymentMethodsList, paymentBroadcastReceivers.getPaymentMethodCallback()); + availablePaymentMethods, paymentBroadcastReceivers.getPaymentMethodCallback()); } final IntentFilter intentFilter = new IntentFilter(PAYMENT_METHOD_SELECTED_INTENT); @@ -256,19 +256,25 @@ private void unregisterBroadcastReceivers() { private void fetchPaymentMethods() { if (paymentResponse != null) { - List unfilteredPaymentMethods = paymentResponse.getAvailablePaymentMethods(); this.preferredPaymentMethods = paymentResponse.getPreferredPaymentMethods(); - Observable> listObservable = ModuleAvailabilityUtil.filterPaymentMethods(context, - unfilteredPaymentMethods); - listObservable.subscribe(new Consumer>() { - @Override - public void accept(List filteredPaymentMethods) { - filteredPaymentMethods.removeAll(Collections.singleton(null)); - PaymentStateHandler.this.filteredPaymentMethodsList.clear(); - PaymentStateHandler.this.filteredPaymentMethodsList.addAll(filteredPaymentMethods); - paymentProcessorStateMachine.onTrigger(PaymentTrigger.PAYMENT_METHODS_AVAILABLE); - } - }); + + if (merchantPaymentRequestDetailsListener == null) { + List unfilteredPaymentMethods = paymentResponse.getAvailablePaymentMethods(); + Observable> listObservable = ModuleAvailabilityUtil.filterPaymentMethods(context, + unfilteredPaymentMethods); + listObservable.subscribe(new Consumer>() { + @Override + public void accept(List filteredPaymentMethods) { + filteredPaymentMethods.removeAll(Collections.singleton(null)); + PaymentStateHandler.this.availablePaymentMethods.clear(); + PaymentStateHandler.this.availablePaymentMethods.addAll(filteredPaymentMethods); + paymentProcessorStateMachine.onTrigger(PaymentTrigger.PAYMENT_METHODS_AVAILABLE); + } + }); + } else { + this.availablePaymentMethods = paymentResponse.getAvailablePaymentMethods(); + paymentProcessorStateMachine.onTrigger(PaymentTrigger.PAYMENT_METHODS_AVAILABLE); + } } } @@ -503,7 +509,7 @@ public void onSuccess(byte[] response) { for (PaymentRequestDetailsListener detailsListener : paymentRequestDetailsListeners) { detailsListener.onPaymentMethodSelectionRequired(paymentRequest, preferredPaymentMethods, - filteredPaymentMethodsList, paymentBroadcastReceivers.getPaymentMethodCallback()); + availablePaymentMethods, paymentBroadcastReceivers.getPaymentMethodCallback()); } return; } diff --git a/adyen-core/src/main/java/com/adyen/core/models/PaymentMethod.java b/adyen-core/src/main/java/com/adyen/core/models/PaymentMethod.java index 8eaefa91d7..77b0f6bffc 100644 --- a/adyen-core/src/main/java/com/adyen/core/models/PaymentMethod.java +++ b/adyen-core/src/main/java/com/adyen/core/models/PaymentMethod.java @@ -97,6 +97,7 @@ static PaymentMethod createPaymentMethod(final JSONObject paymentMethodJSON, fin configuration.merchantId = configurationJson.optString("merchantIdentifier"); configuration.merchantName = configurationJson.optString("merchantName"); configuration.publicKey = configurationJson.optString("publicKey").replaceAll("\\r\\n", ""); + configuration.environment = configurationJson.optString("environment"); } if (paymentMethod.inputDetails != null) { @@ -313,6 +314,7 @@ public static final class Configuration implements Serializable { private String publicKey; private String cvcOptional; private String noCVC; + private String environment; //For AndroidPay private Configuration() { } @@ -335,6 +337,10 @@ public String getCvcOptional() { public String getNoCVC() { return noCVC; } + + public String getEnvironment() { + return environment; + } } @Override diff --git a/adyen-core/src/main/java/com/adyen/core/utils/AsyncImageDownloader.java b/adyen-core/src/main/java/com/adyen/core/utils/AsyncImageDownloader.java index 7436a42c10..55b780d7b0 100644 --- a/adyen-core/src/main/java/com/adyen/core/utils/AsyncImageDownloader.java +++ b/adyen-core/src/main/java/com/adyen/core/utils/AsyncImageDownloader.java @@ -10,16 +10,23 @@ import android.util.Log; import android.widget.ImageView; +import com.adyen.core.internals.TLSSocketFactory; + import java.io.FileNotFoundException; -import java.io.InputStream; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLSocketFactory; import io.reactivex.Observable; import io.reactivex.ObservableEmitter; import io.reactivex.ObservableOnSubscribe; import io.reactivex.Observer; +import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import io.reactivex.android.schedulers.AndroidSchedulers; /** * Utility class for downloading images and assigning them to ImageViews. @@ -33,6 +40,16 @@ public final class AsyncImageDownloader { @NonNull private static final LruCache BITMAP_LRU_CACHE = new LruCache<>(MAX_NUMBER_OF_IMAGES_TO_BE_CACHED); + private static final SSLSocketFactory SSL_SOCKET_FACTORY; + + static { + try { + SSL_SOCKET_FACTORY = new TLSSocketFactory(); + } catch (KeyManagementException | NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + public interface ImageListener { void onImage(Bitmap bitmap, String url); } @@ -162,7 +179,7 @@ private static Bitmap retrieveImage(Context context, String url, Bitmap fallback /** * This method issues an http request to retrieve the image at the given url. - * If successfull, the image will be saved in the local cache (static variable) and + * If successful, the image will be saved in the local cache (static variable) and * also in the sharedpreferences. * @param context Application context * @param url Url of the icon to be loaded @@ -171,10 +188,14 @@ private static Bitmap retrieveImage(Context context, String url, Bitmap fallback */ private static Bitmap downloadImage(Context context, String url, Bitmap fallbackImage) { Bitmap icon = null; + HttpsURLConnection urlConnection = null; try { Log.d(TAG, "Downloading image from: " + url); - InputStream in = new java.net.URL(url).openStream(); - icon = BitmapFactory.decodeStream(in); + urlConnection = (HttpsURLConnection) new URL(url).openConnection(); + urlConnection.setSSLSocketFactory(SSL_SOCKET_FACTORY); + urlConnection.connect(); + + icon = BitmapFactory.decodeStream(urlConnection.getInputStream()); BITMAP_LRU_CACHE.put(url, icon); IconStorage.storeIcon(context, icon, url); } catch (@NonNull final FileNotFoundException fileNotFoundException) { @@ -190,6 +211,10 @@ private static Bitmap downloadImage(Context context, String url, Bitmap fallback } catch (Exception e) { Log.e("Error", e.getMessage()); e.printStackTrace(); + } finally { + if (urlConnection != null) { + urlConnection.disconnect(); + } } return icon; } diff --git a/adyen-ui/src/androidTest/java/com/adyen/ui/ExampleInstrumentedTest.java b/adyen-ui/src/androidTest/java/com/adyen/ui/ExampleInstrumentedTest.java deleted file mode 100644 index 0f49b9a2fc..0000000000 --- a/adyen-ui/src/androidTest/java/com/adyen/ui/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.adyen.ui; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.assertEquals; - -/** - * Instrumentation test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.adyen.ui.test", appContext.getPackageName()); - } -} diff --git a/adyen-ui/src/main/java/com/adyen/ui/DefaultPaymentRequestDetailsListener.java b/adyen-ui/src/main/java/com/adyen/ui/DefaultPaymentRequestDetailsListener.java index 36eae84c55..b77a5e76f5 100644 --- a/adyen-ui/src/main/java/com/adyen/ui/DefaultPaymentRequestDetailsListener.java +++ b/adyen-ui/src/main/java/com/adyen/ui/DefaultPaymentRequestDetailsListener.java @@ -11,6 +11,7 @@ import android.support.annotation.NonNull; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; +import android.widget.Toast; import com.adyen.core.PaymentRequest; import com.adyen.core.interfaces.PaymentDetailsCallback; @@ -166,25 +167,23 @@ public void onPaymentDetailsRequired(@NonNull PaymentRequest paymentRequest, intent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); context.startActivity(intent); } else if (paymentRequest.getPaymentMethod().getPaymentModule() != null) { - PaymentMethodService paymentMethodService; + PaymentMethodService paymentMethodService = null; try { paymentMethodService = ModuleAvailabilityUtil.getModulePaymentService( paymentRequest.getPaymentMethod().getPaymentModule()); } catch (@NonNull final ClassNotFoundException e) { Log.e(TAG, "requestPaymentMethodDetails(): Payment module not found.", e); - return; } catch (@NonNull final IllegalAccessException e) { Log.e(TAG, "requestPaymentMethodDetails(): IllegalAccessException occurred", e); - return; } catch (@NonNull final InstantiationException e) { Log.e(TAG, "requestPaymentMethodDetails(): InstantiationException occurred", e); - return; } catch (@NonNull NullPointerException nullPointerException) { Log.e(TAG, "requestPaymentMethodDetails(): Null pointer exception: ", nullPointerException); - return; } - if (paymentMethodService != null) { + if (paymentMethodService == null) { + Toast.makeText(context, "Payment method not supported.", Toast.LENGTH_LONG).show(); + } else { // Pass the request to the correct module. The rest will be handled by the module. paymentMethodService.process(context, paymentRequest, paymentRequest.getPaymentRequestListener(), null); } diff --git a/adyen-ui/src/main/java/com/adyen/ui/views/CVCEditText.java b/adyen-ui/src/main/java/com/adyen/ui/views/CVCEditText.java index bb4a8c78d7..fa9ba27a9e 100644 --- a/adyen-ui/src/main/java/com/adyen/ui/views/CVCEditText.java +++ b/adyen-ui/src/main/java/com/adyen/ui/views/CVCEditText.java @@ -132,7 +132,8 @@ public void setMaxLength(final int newMaxLength) { } public boolean hasValidInput() { - return (this.getText().toString().length() == maxLength) || optional; + int cvcLength = this.getText().toString().length(); + return optional ? ((cvcLength == maxLength) || (cvcLength == 0)) : (cvcLength == maxLength); } public void setOptional(final boolean optional) { diff --git a/adyen-ui/src/main/java/com/adyen/ui/views/ExpiryDateEditText.java b/adyen-ui/src/main/java/com/adyen/ui/views/ExpiryDateEditText.java index d8784790f8..9d48d51b6c 100644 --- a/adyen-ui/src/main/java/com/adyen/ui/views/ExpiryDateEditText.java +++ b/adyen-ui/src/main/java/com/adyen/ui/views/ExpiryDateEditText.java @@ -106,9 +106,9 @@ public void afterTextChanged(Editable s) { } if (s.length() == 2 && !deleted) { - if (s.toString().matches("\\d/")) { + if (s.toString().matches("\\d[^\\d]")) { s.insert(0, "0"); - } else if (!s.toString().contains(separatorString)) { + } else if (!s.toString().matches("\\d*[^\\d]\\d*")) { s.append(SEPARATOR_CHAR); } } @@ -157,7 +157,7 @@ public void afterTextChanged(Editable s) { } private boolean isInputDateValid(String dateStr) { - if (dateStr.length() == 5) { + if (dateStr.matches("(0?[1-9]|1[0-2])/[\\d]{2}")) { Calendar c = Calendar.getInstance(); int currentYear = c.get(Calendar.YEAR) - 2000; int currentMonth = c.get(Calendar.MONTH) + 1; //month january is 0 diff --git a/app/src/androidTest/java/com/adyen/checkout/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/adyen/checkout/ExampleInstrumentedTest.java deleted file mode 100644 index 6c15ecf716..0000000000 --- a/app/src/androidTest/java/com/adyen/checkout/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.adyen.checkout; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.assertEquals; - -/** - * Instrumentation test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.adyen.checkout", appContext.getPackageName()); - } -} diff --git a/app/src/androidTest/java/com/adyen/checkout/PaymentAppTest.java b/app/src/androidTest/java/com/adyen/checkout/PaymentAppTest.java index c6001c968e..2c051cea38 100644 --- a/app/src/androidTest/java/com/adyen/checkout/PaymentAppTest.java +++ b/app/src/androidTest/java/com/adyen/checkout/PaymentAppTest.java @@ -1,5 +1,7 @@ package com.adyen.checkout; +import android.content.Intent; +import android.content.pm.ActivityInfo; import android.support.test.espresso.Espresso; import android.support.test.espresso.NoActivityResumedException; import android.support.test.filters.LargeTest; @@ -13,15 +15,18 @@ import com.adyen.testutils.RetryTest; import org.junit.After; +import org.junit.Before; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Calendar; import java.util.Collection; import java.util.Random; import static android.support.test.InstrumentationRegistry.getInstrumentation; +import static android.support.test.InstrumentationRegistry.getTargetContext; import static android.support.test.espresso.Espresso.onData; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.clearText; @@ -71,6 +76,14 @@ public void tearDown() throws Exception { closeAllActivities(getInstrumentation()); } + @Before + public void setUp() throws Throwable { + UiDevice.getInstance(getInstrumentation()).wakeUp(); + Intent intent = new Intent(getTargetContext(), MainActivity.class); + mainActivityRule.launchActivity(intent); + mainActivityRule.getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + @Test public void testRefusedVisaPayment() throws Exception { checkCardPayment("13", "EUR", "Credit Card", "4444333322221111", "12/18", "722", Payment.PaymentStatus.REFUSED.toString()); @@ -145,6 +158,46 @@ public void testCardExpDate() throws Exception { cancelCreditCardPayment(); } + @Test + public void testCardExpiryExtended() throws Exception { + goToCreditCardFragment(); + + checkCreditCardPayButtonIsEnabled(false); + + onView(withId(R.id.adyen_credit_card_no)).perform(typeText("4111111111111111")); + onView(withId(R.id.adyen_credit_card_cvc)).perform(typeText("737")); + + int startYear = Calendar.getInstance().get(Calendar.YEAR); + int startMonth = Calendar.getInstance().get(Calendar.MONTH) + 1; + + int start = startYear * 12 + startMonth - 3; + int end = 2030 * 12; + + for (int i = start; i <= end; i++) { + int month = i % 12; + if (month == 0) { + month = 12; + } + int year = i / 12 - 2000; + + if (month > 9) { + String text = month + "" + year; + onView(withId(R.id.adyen_credit_card_exp_date)).perform(clearText(), typeText(text)); + checkCreditCardPayButtonIsEnabled(true); + } else { + String text = "0" + month + year; + onView(withId(R.id.adyen_credit_card_exp_date)).perform(clearText(), typeText(text)); + checkCreditCardPayButtonIsEnabled(true); + + if (month > 1) { + text = month + "" + year; + onView(withId(R.id.adyen_credit_card_exp_date)).perform(clearText(), typeText(text)); + checkCreditCardPayButtonIsEnabled(true); + } + } + } + } + @Test public void testCVCField() throws Exception { goToCreditCardFragment(); @@ -340,7 +393,7 @@ public void testNoCVC() throws Exception { onView(withText(equalToIgnoringCase("Bancontact card"))).perform(scrollTo(), click()); onView(withId(R.id.adyen_credit_card_no)).perform(clearText(), typeText("6703444444444449"), closeSoftKeyboard()); - onView(withId(R.id.adyen_credit_card_exp_date)).perform(typeText("818"), + onView(withId(R.id.adyen_credit_card_exp_date)).perform(clearText(), typeText("818"), closeSoftKeyboard()); checkCreditCardPayButtonIsEnabled(true); } @@ -352,7 +405,16 @@ public void testOptionalCVC() throws Exception { waitForText("CVC/CVV"); onView(withId(R.id.adyen_credit_card_no)).perform(clearText(), typeText("6731 0123 4567 8906"), closeSoftKeyboard()); - onView(withId(R.id.adyen_credit_card_exp_date)).perform(typeText("818"), + onView(withId(R.id.adyen_credit_card_exp_date)).perform(clearText(), typeText("818"), + closeSoftKeyboard()); + checkCreditCardPayButtonIsEnabled(true); + onView(withId(R.id.adyen_credit_card_cvc)).perform(clearText(), typeText("7"), + closeSoftKeyboard()); + checkCreditCardPayButtonIsEnabled(false); + onView(withId(R.id.adyen_credit_card_cvc)).perform(clearText(), typeText("73"), + closeSoftKeyboard()); + checkCreditCardPayButtonIsEnabled(false); + onView(withId(R.id.adyen_credit_card_cvc)).perform(clearText(), typeText("737"), closeSoftKeyboard()); checkCreditCardPayButtonIsEnabled(true); } diff --git a/build.gradle b/build.gradle index 06fb4f4e6d..f9df5500d7 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.1' + classpath 'com.android.tools.build:gradle:2.3.3' } } @@ -30,8 +30,8 @@ ext { minSdkVersion = 16 targetSdkVersion = 25 - versionCode = 12 - versionName = "1.10.0" + versionCode = 14 + versionName = "1.11.1" release_debuggable = false release_minifyEnabled = false @@ -55,7 +55,7 @@ jacoco { subprojects { - def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*', '**/checkout/**', '**/customuiapplication/**', '**/checkoutdemo/**', '**/customwithadyenui/**'] + def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*', '**/checkout/**', '**/customuiapplication/**', '**/checkoutdemo/**', '**/customwithcheckoutui/**'] apply plugin: 'checkstyle' checkstyle { @@ -162,6 +162,15 @@ task jacocoOverallTestReport(type: JacocoReport, group: 'Coverage reports') { getReports().getHtml().setDestination(file("$buildDir/reports/jacocoOverallTestReport/html")) getReports().getXml().setDestination(file("$buildDir/reports/jacocoOverallTestReport/merged.xml")) + + doLast { + publishedProjects.forEach { publishedProject -> + copy { + from "$publishedProject.buildDir/reports/" + into "$buildDir/reports/$publishedProject.name/" + } + } + } } task jacocoOverallUnitTestReport(type: JacocoReport, group: 'Coverage reports') { @@ -259,7 +268,7 @@ task parseXml() { } def parsedProjectXml = (new XmlParser()).parse('api_configuration.xml') def env = project.findProperty('serverEnv') ?: "TEST" - def type = project.findProperty('serverType') ?: "MERCHANT_SERVER" + def type = project.findProperty('serverType') ?: "DIRECT_API" def url = parsedProjectXml[type][env].URL.text() def apiKey = parsedProjectXml[type][env].API_KEY.text() diff --git a/checkoutdemo/build.gradle b/checkoutdemo/build.gradle index 862f7cbce0..7c5c64ed27 100644 --- a/checkoutdemo/build.gradle +++ b/checkoutdemo/build.gradle @@ -37,8 +37,8 @@ dependencies { }) compile "com.android.support:appcompat-v7:${rootProject.supportLibVersion}" testCompile "junit:junit:${rootProject.jUnitVersion}" - compile 'com.adyen.checkout:core:1.10.0' - compile 'com.adyen.checkout:ui:1.10.0' - compile 'com.adyen.checkout:utils:1.10.0' - compile 'com.adyen.checkout:cardscan:1.10.0' + compile 'com.adyen.checkout:core:1.11.0' + compile 'com.adyen.checkout:ui:1.11.0' + compile 'com.adyen.checkout:utils:1.11.0' + compile 'com.adyen.checkout:cardscan:1.11.0' } diff --git a/customuiapplication/src/androidTest/java/com/adyen/customuiapplication/ExampleInstrumentedTest.java b/customuiapplication/src/androidTest/java/com/adyen/customuiapplication/ExampleInstrumentedTest.java deleted file mode 100644 index 56a6b37977..0000000000 --- a/customuiapplication/src/androidTest/java/com/adyen/customuiapplication/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.adyen.customuiapplication; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static junit.framework.Assert.assertEquals; - -/** - * Instrumentation test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.adyen.customuiapplication", appContext.getPackageName()); - } -} diff --git a/customuiapplication/src/androidTest/java/com/adyen/customuiapplication/PaymentAppTest.java b/customuiapplication/src/androidTest/java/com/adyen/customuiapplication/PaymentAppTest.java index 8a6666f436..743759267d 100644 --- a/customuiapplication/src/androidTest/java/com/adyen/customuiapplication/PaymentAppTest.java +++ b/customuiapplication/src/androidTest/java/com/adyen/customuiapplication/PaymentAppTest.java @@ -4,6 +4,7 @@ import android.support.test.filters.LargeTest; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; +import android.support.test.uiautomator.UiDevice; import com.adyen.core.models.Payment; import com.adyen.core.models.PaymentMethod; @@ -12,6 +13,7 @@ import org.hamcrest.Matcher; import org.junit.After; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -51,6 +53,11 @@ public class PaymentAppTest { @Rule public RetryTest retry = new RetryTest(5); + @Before + public void setUp() throws Throwable { + UiDevice.getInstance(getInstrumentation()).wakeUp(); + } + @After public void tearDown() throws Exception { closeAllActivities(getInstrumentation()); diff --git a/customwithcheckoutui/src/androidTest/java/com/example/customwithcheckoutui/ExampleInstrumentedTest.java b/customwithcheckoutui/src/androidTest/java/com/example/customwithcheckoutui/ExampleInstrumentedTest.java deleted file mode 100644 index c7d231a0af..0000000000 --- a/customwithcheckoutui/src/androidTest/java/com/example/customwithcheckoutui/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.example.customwithcheckoutui; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.assertEquals; - -/** - * Instrumentation test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.example.customwithadyenui", appContext.getPackageName()); - } -} diff --git a/testutils/src/androidTest/java/com/adyen/testutils/ExampleInstrumentedTest.java b/testutils/src/androidTest/java/com/adyen/testutils/ExampleInstrumentedTest.java deleted file mode 100644 index f35516068a..0000000000 --- a/testutils/src/androidTest/java/com/adyen/testutils/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.adyen.testutils; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static junit.framework.Assert.assertEquals; - -/** - * Instrumentation test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("com.adyen.testutils.test", appContext.getPackageName()); - } -}