Reader SDK

Build on Android

Android
Reader SDK

Prerequisites and assumptions
Permalink Get a link to this section

To build with Reader SDK, the following must be true:

  • You have a Square Account enabled for payment processing. If you have not enabled payment processing on your account (or you are not sure), visit squareup.com/activate.

  • Your application minSdkVersion is API 21 (Lollipop 5.0) or higher.

  • Your application targetSdkVersion is API 28 (Pie, 9.0) or lower.

  • Your application uses AndroidX or enables Jetifier in gradle.properties.

  • You are using the Android Gradle Plugin version 3.0.0 or greater. Reader SDK may work with 2.3.0 or greater, but stability is not guaranteed.

  • Your application uses Google Play Services version 16.0.1. Square cannot guarantee that the SDK will work with different versions of these libraries.

  • You are not using Proguard for code optimization. Compressing the Reader SDK binary removes critical bytecode elements and results in runtime errors.


Additionally, this guide makes the following assumptions:

  • Your version of Reader SDK adheres to the Square update policy. To limit risk to developers and their users, Square enforces an update policy for Reader SDK requiring developers keep their version of Reader SDK current.

  • You are generally familiar with developing apps on Android. If you are new to Android development, we recommend reading the Getting Started Guide at the Android Developers site before continuing.

Important

Reader SDK is not supported on Android versions below API 19 (KitKat, 4.4). Square does not recommend targeting Android versions above API 28 (Oreo, 8.1) with Reader SDK.

Device permissions
Permalink Get a link to this section

To work with Square Readers, apps must also have the following device permissions. If the required device permissions are not granted when the checkout flow initiates, Reader SDK prompts the user to grant the necessary permissions.

Android device permissionPurpose
LocationTo confirm payments are occurring in a supported country.
AudioTo connect Magstripe Readers.
BluetoothTo connect Contactless Readers.
Device StorageTo store information during checkout.
Phone AccessTo identify the device sending information to Square servers.

Information you will need
Permalink Get a link to this section

To use the steps in this guide you will need the following information:

  • Your application ID. Find your application ID on the Credentials setting page of your Square application.

  • Your Reader SDK repo password. Find your Reader SDK repo password on the Reader SDK setting page of your Square application.

Step 1: Request Reader SDK credentials
Permalink Get a link to this section

  1. Open the Square Application Dashboard. You will be prompted to login or create a new account.

  2. Create a new Square application.

  3. Click on the new application to bring up the Square application settings pages.

  4. Open the Reader SDK page and click "Request Credentials" to generate your Reader SDK repository password.

readersdk-settings-page

Step 2: Configure Gradle for Reader SDK
Permalink Get a link to this section

  1. Update the gradle.properties file in the root folder of your project to increase the max heap size provided to the Gradle daemon and set variables for the Square application ID and repository password:

  2. Add the Reader SDK variables from your properties file and confirm that the Google repository is set properly in the build.gradle file of your :app module:


Update the gradle.properties file

SQUARE_READER_SDK_APPLICATION_ID={APPLICATION ID FROM STEP 1}
SQUARE_READER_SDK_REPOSITORY_PASSWORD={REPO PASSWORD FROM STEP 1}
org.gradle.jvmargs=-Xmx4g

Add the Reader SDK variables

 repositories {
  google()
  maven {
    url "https://sdk.squareup.com/android"
    credentials {
      username SQUARE_READER_SDK_APPLICATION_ID
      password SQUARE_READER_SDK_REPOSITORY_PASSWORD
    }
  }
  jcenter()
}

Step 3: Configure the build dependencies
Permalink Get a link to this section

Did you know?

If you run into compile errors, make sure that you are using AndroidX across all your dependencies by running ./gradlew app:dependencies.

  1. Reader SDK and its dependencies contain more than 65k methods, so your build script must enable Multidex. If your minSdkVersion is less than 21, you also need to include the multidex dependency.

android {
  defaultConfig {
    minSdkVersion 21
    targetSdkVersion 28
    multiDexEnabled true
  }
}

dependencies {
  // Add this dependency if your minSdkVersion < 21
  implementation 'androidx.multidex:multidex:2.0.0'
  // ...
}
  1. Add a compile option to support Java version 1.8 in the build.gradle file for your :app module.

android {
  // ...
  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }
  // ...
}

  1. Configure the Multidex options:

android {
  // ...
  dexOptions {
    // Ensures incremental builds remain fast
    preDexLibraries true
    // Required to build with Reader SDK
    jumboMode true
    // Required to build with Reader SDK
    keepRuntimeAnnotatedClasses false
  }
  // ...
}
  1. Add the Reader SDK dependencies:

dependencies {
  def readerSdkVersion = "1.3.3"
  implementation "com.squareup.sdk.reader:reader-sdk-$SQUARE_READER_SDK_APPLICATION_ID:$readerSdkVersion"
  runtimeOnly "com.squareup.sdk.reader:reader-sdk-internals:$readerSdkVersion"
  // ...
}

Step 4: Extend the Android Application class
Permalink Get a link to this section

  1. Create an Application class that extends android.app.Application and specify the android:name property for the <application> node in AndroidManifest.xml (for more help with this step, see Understanding the Android Application Class on Codepath).

  2. Import and initialize Reader SDK:

import com.squareup.sdk.reader.ReaderSdk;
import com.squareup.sdk.reader.authorization.AuthorizationManager;
import com.squareup.sdk.reader.authorization.AuthorizeErrorCode;
import com.squareup.sdk.reader.authorization.Location;
import com.squareup.sdk.reader.core.CallbackReference;
import com.squareup.sdk.reader.core.Result;
import com.squareup.sdk.reader.core.ResultError;

public class ExampleApplication extends Application {

  @Override public void onCreate() {
    super.onCreate();
    ReaderSdk.initialize(this);
  }

  @Override protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    // Required if minSdkVersion < 21
    MultiDex.install(this);
  }
}

Step 5: Add code to request and use a mobile authorization code
Permalink Get a link to this section

To authorize the SDK, you must build an authorization service to retrieve a mobile authorization code with the Mobile Authorization API and return it to your application.

Mobile authorization codes are short lived and should be used immediately to authorize Reader SDK. Authorization is valid until it is explicitly revoked by calling deauthorize or your application fails to take a payment within 90 days. Mobile authorization codes do not need to be manually refreshed under normal operations.

Create a new AuthorizeActivity with 2 skeleton functions (retrieveAuthorizationCode() and onAuthorizationCodeRetrieved()), and then customize them to call your authorization service:

Did you know?

You can also generate mobile authorization codes manually with a command line tool. See Request a mobile authorization code on the command line in the Mobile Authorization API guide for more information.

public class AuthorizeActivity extends Activity {

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.authorize_activity);

    View authorizeButton = findViewById(R.id.authorize_button);
    authorizeButton.setOnClickListener(view -> retrieveAuthorizationCode());
  }

  private void retrieveAuthorizationCode() {
    // TODO: Asynchronous code to retrieve a mobile authorization code.
    // Calls onAuthorizationCodeRetrieved(String) with the resulting code.
  }

  private void onAuthorizationCodeRetrieved(String authorizationCode) {
    showAuthorizationInProgress(true);
    ReaderSdk.authorizationManager().authorize(authorizationCode);
  }

  private void showAuthorizationInProgress(boolean inProgress) {
    // TODO: Display or hide a loading indicator
  }
}

Step 6: Add code to handle authorization results
Permalink Get a link to this section

  1. Add a callback (onAuthorizeResult()) to the Authorization Manager in the onCreate method of your authorization activity using AuthorizationManager.addAuthorizeCallback().

  2. Implement the onAuthorizeResult() callback. AuthorizeCallback.onResult() is invoked asynchronously on the main thread with a Result object. The result includes the authorized Location (in case of success) or a ResultError (in case of error).

  3. Add code to clear the callback reference in your onDestroy() method to avoid memory leaks.


Add onAuthorizeResult callback to Authorization Manager

//
public class AuthorizeActivity extends Activity {

  // ...

  private CallbackReference authorizeCallbackRef;

  @Override protected void onCreate(Bundle savedInstanceState) {

    //...

    AuthorizationManager authManager = ReaderSdk.authorizationManager();
    authorizeCallbackRef =
        authManager.addAuthorizeCallback(this::onAuthorizeResult);
  }

  // ...

  private void onAuthorizeResult(Result<Location,
      ResultError<AuthorizeErrorCode>> result) {
    // TODO: Handle the authorization result
  }

Implement the onAuthorizeResult() callback

  private void onAuthorizeResult(Result<Location,
      ResultError<AuthorizeErrorCode>> result) {
    showAuthorizationInProgress(false);
    if (result.isSuccess()) {
      goToCheckoutActivity();
    } else {
      ResultError<AuthorizeErrorCode> error = result.getError();
      switch (error.getCode()) {
        case NO_NETWORK:
          showDialog(getString(R.string.no_network), error.getMessage());
          break;
        case USAGE_ERROR:
          String dialogMessage = error.getMessage();
          if (BuildConfig.DEBUG) {
            dialogMessage += "\n\nDebug Message: " + error.getDebugMessage();
            Log.d("Auth", error.getDebugCode() + ", " + error.getDebugMessage());
          }
          showDialog(getString(R.string.error_dialog_title), dialogMessage);
          break;
      }
    }
 }

Clear the callback reference in your onDestroy() method

public class AuthorizeActivity extends Activity {

  // ...

  @Override protected void onDestroy() {
    super.onDestroy();
    authorizeCallbackRef.clear();
  }
}

Step 7: Create a CheckoutActivity
Permalink Get a link to this section

Create a new activity, CheckoutActivity, with a button that calls startCheckout() to begin the checkout flow:

import com.squareup.sdk.reader.checkout.AdditionalPaymentType;
import com.squareup.sdk.reader.checkout.CheckoutErrorCode;
import com.squareup.sdk.reader.checkout.CheckoutManager;
import com.squareup.sdk.reader.checkout.CheckoutParameters;
import com.squareup.sdk.reader.checkout.CheckoutResult;
import com.squareup.sdk.reader.checkout.CurrencyCode;
import com.squareup.sdk.reader.checkout.Money;
import com.squareup.sdk.reader.core.CallbackReference;
import com.squareup.sdk.reader.core.Result;
import com.squareup.sdk.reader.core.ResultError;

public class CheckoutActivity extends Activity {

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (!ReaderSdk.authorizationManager().getAuthorizationState().isAuthorized()) {
      goToAuthorizeActivity();
    }

    setContentView(R.layout.checkout_activity);

    View startCheckoutButton = findViewById(R.id.start_checkout_button);
    startCheckoutButton.setOnClickListener(view -> startCheckout());
  }

  private void goToAuthorizeActivity() {
      // TODO: Reader SDK is not authorized, move to Authorize Activity
  }
}

Step 8: Add code to start the checkout flow
Permalink Get a link to this section

  1. Implement the startCheckout() method by adding code to build the CheckoutParameters object and configure the checkout experience. In the code below we use CurrencyCode#current to obtain the currency code of the authorized location and set CASH as one of the additional tender types, which lets us test payments without charging a card.

  2. Add a callback (onCheckoutResult()) to the Checkout Manager in the onCreate method of CheckoutActivity using CheckoutManager.addCheckoutActivityCallback.

  3. Add code to clear the checkout callback reference in your onDestroy() method to avoid memory leaks.

Implement the startCheckout() method

public class CheckoutActivity extends Activity {
 // ...

 private CallbackReference checkoutCallbackRef;

 private void startCheckout() {
   CheckoutManager checkoutManager = ReaderSdk.checkoutManager();
   Money amountMoney = new Money(100, CurrencyCode.current());
   CheckoutParameters.Builder parametersBuilder =
       CheckoutParameters.newBuilder(amountMoney);
   if (BuildConfig.DEBUG) {
     parametersBuilder.additionalPaymentTypes(AdditionalPaymentType.CASH);
   }
   checkoutManager.startCheckoutActivity(this, parametersBuilder.build());
 }
}

Add onCheckoutResult() to the Checkout Manager

  @Override protected void onCreate(Bundle savedInstanceState) {

    // ...

    CheckoutManager checkoutManager = ReaderSdk.checkoutManager();
    checkoutCallbackRef =   
        checkoutManager.addCheckoutActivityCallback(this::onCheckoutResult);

  }
}

Clear the checkout callback reference in your onDestroy() method

public class CheckoutActivity extends Activity {

  // ...

    @Override protected void onDestroy() {
    super.onDestroy();
    checkoutCallbackRef.clear();
  }
}

Step 9: Add code to handle checkout results
Permalink Get a link to this section

Implement the onCheckoutResult callback to parse the checkout response. CheckoutActivityCallback.onResult() is invoked asynchronously on the main thread with a Result object. The result includes the CheckoutResult (in case of success) or a ResultError (in case of error):

public class CheckoutActivity extends Activity {
  // ...

  private void onCheckoutResult(Result<CheckoutResult, ResultError<CheckoutErrorCode>> result) {
    if (result.isSuccess()) {
      CheckoutResult checkoutResult = result.getSuccessValue();
      showCheckoutResult(checkoutResult);
    } else {
      ResultError<CheckoutErrorCode> error = result.getError();

      switch (error.getCode()) {
        case SDK_NOT_AUTHORIZED:
          goToAuthorizeActivity();
          break;
        case CANCELED:
          Toast.makeText(this, "Checkout canceled", Toast.LENGTH_SHORT).show();
          break;
        case USAGE_ERROR:
          showErrorDialog(error);
          break;
      }
    }
  }

  private void showCheckoutResult(CheckoutResult checkoutResult) {
    // TODO: Display the checkout result.
  }

  private void showErrorDialog(ResultError<?> error) {
    String dialogMessage = error.getMessage();
    if (BuildConfig.DEBUG) {
      dialogMessage += "\n\nDebug Message: " + error.getDebugMessage();
      Log.d("Checkout", error.getDebugCode() + ", " + error.getDebugMessage());
    }
    showDialog(getString(R.string.error_dialog_title), dialogMessage);
  }

  private void showDialog(CharSequence title, CharSequence message) {
    // TODO: Display a dialog to the user.
  }
}