Android SDK

This SDK provides an UI to take required information from user to execute transaction.

Sample Project to implement the Android SDK here.


Latest Version

Download the latest version here.

Latest Released version logs on Github release page.




Android SDK Changes


version 1.28.0:


  • If using UIKit and the device is going to use 3G/4G internet (Data network), it is required on Android to have READ_PHONE_STATE permission in order to access network type of the device. the purpose of READ_PHONE_STATE permission is to capture network on event tracker. Therefore, before launching the UI kit, it is mandatory to check if this permission has been granted or not. If it has not been granted, we need to keep asking user. It is not needed for WIFI connection and version below 1.28.0.

Request Permission before launching UI kit for Android SDK version 1.28.0 and above

if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
     ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE}, 101);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
  ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE}, 101);
}
  • If the Host app got build error with message Invoke-customs are only supported starting with Android O a block of code to specify java version is needed on build.gradle.

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

Starting version 1.29.0:

  • To avoid crash in SDK Version 1.29.0, it will make optional to implement READ_PHONE_STATE permission, so the host app is not required to ask the user to give the permission.

  • Since we increase the localizationVersion to 1.2.10 it required to add multiDexEnabled true in Host App build.gradle


[Important] Starting version 1.34.0:


  • To avoid inconsistencies for e-money method on tablet, starting from version 1.34.0 the payment type for gopay on tablet is no longer gopay but changed to qris same as ShopeePay using qris
  • The JSON fields format response is different between "payment_type": "qris" and "payment_type": "gopay" . This will affect Webhook/HTTP Notification and Get Status API Response. Refer to the notification example section for details.
  • Due to the difference in response between qris and gopay, you need to remap the response for the Notification and Get Status API. So please ensure that your system’s implementation can handle this behavior without breaking your system & its payment flow. Especially on the following parts (if applicable):
    • Your Notifications Handler implementation logic.
    • Get Status API implementation logic.
    • Reconciliation implementation logic.
  • For more details regarding QRIS related behavior, please refer to this section

[Important] Starting version 2.0.0:


  • Midtrans SDK 2.0.0 is a major revamp for Midtrans SDK that includes UI changes and updates to improve user experience.
  • There are two scenarios for the existing users to write code using this Revamp SDK:
    • Using Existing Code: For existing users who want to continue using their existing code, simply upgrade the SDK version in the build.gradle file. This revamp supports backward compatibility, so there is no need to make any changes to the existing code.
    • Using Revamped Code: For existing users who want to take advantage of the new UI changes and improvements, we recommend using the revamped code. This approach will ensure that future updates to the SDK, since the next updates will released with the revamp-first mindset.
  • For new users who are just starting to use the Midtrans Mobile SDK, we highly recommend using the revamped approach. This will ensure that you have access to the latest updates and improvements to the SDK.

🚧

Please make sure to follow the setup instructions and guidelines provided in the Snap SDK documentation to ensure a smooth integration process. If you have any questions or concerns, don't hesitate to reach out to our support team.




Android Permission


UI KIT

  • ACCESS_NETWORK_STATE
  • READ_PHONE_STATE



Installation


Add SDK installation following into your build.gradle


Midtrans Maven Repository

repositories {
    jcenter()
        maven { url "https://jitpack.io" }
    }
repositories {
  jcenter()
  	maven { url "https://jitpack.io" }
  }

Sample SDK Sandbox Dependencies

dependencies {
    // For using the Midtrans Sandbox
    implementation 'com.midtrans:uikit:2.0.0-SANDBOX' // change the number to the latest version
  }
dependencies {
	// For using the Midtrans Sandbox
    implementation 'com.midtrans:uikit:2.0.0-SANDBOX' // change the number to the latest version
  }

Sample SDK Production Dependencies

dependencies {
    // For using the Midtrans Production
    implementation 'com.midtrans:uikit:2.0.0' // change the number to the latest version
}
dependencies {
	// For using the Midtrans Production
	implementation 'com.midtrans:uikit:1.34.0' // change the number to the latest version
}

You need to add Midtrans SDK inside your app’s module build.gradle. Make sure to use the proper environment (SANDBOX / PRODUCTION).


Midtrans SDK Initialization

UiKitApi.Builder()
        .withMerchantClientKey(CLIENT_KEY) // client_key is mandatory
        .withContext(CONTEXT) // context is mandatory
        .withMerchantUrl(BASE_URL) // set transaction finish callback (sdk callback)
        .enableLog(true) // enable sdk log (optional)
        .withFontFamily(ASSET_FONT)
        .withColorTheme(CustomColorTheme("#FFE51255", "#B61548", "#FFE51255"))
        .build()
setLocaleNew("en") //`en` for English and `id` for Bahasa

...

// function to set the SDK language
private fun setLocaleNew(languageCode: String?) {
    val locales = LocaleListCompat.forLanguageTags(languageCode)
    AppCompatDelegate.setApplicationLocales(locales)
}
SdkUIFlowBuilder.init()
  .setClientKey(CLIENT_KEY) // client_key is mandatory
  .setContext(CONTEXT) // context is mandatory
  .setTransactionFinishedCallback(new TransactionFinishedCallback() {
    @Override
    public void onTransactionFinished(TransactionResult result) {
    	// Handle finished transaction here.
    }
  }) // set transaction finish callback (sdk callback)
  .setMerchantBaseUrl(BASE_URL) //set merchant url (required)
  .enableLog(true) // enable sdk log (optional)
  .setColorTheme(new CustomColorTheme("#FFE51255", "#B61548", "#FFE51255")) // set theme. it will replace theme on snap theme on MAP ( optional)
  .setLanguage("en") //`en` for English and `id` for Bahasa
  .buildSDK();

Then you need to initialize it on your activity or application class.


Note:

  • CONTEXT: Application/activity context
  • CLIENT_KEY: Your midtrans client key (provided in MAP)
  • BASE_URL: Your merchant server URL

Differentiate Sandbox and Production in one app (Optional)


Differentiate Sandbox and Production Flavors

android {
  ...
  // Define Merchant BASE URL and CLIENT KEY for each flavors
  productFlavors {
          sandbox {
              buildConfigField "String", "BASE_URL", "\"https://merchant-url-sandbox.com/\""
              buildConfigField "String", "CLIENT_KEY", "\"VT-CLIENT-sandbox-client-key\""
          }

          production {
              buildConfigField "String", "BASE_URL", "\"https://merchant-url-production.com/\""
              buildConfigField "String", "CLIENT_KEY", "\"VT-CLIENT-production-client-key\""
         }
  }
  ...
}

// Define Midtrans SDK dependencies for each flavors
dependencies {
  ...
  sandboxImplementation 'com.midtrans:uikit:2.0.0-SANDBOX' // change the version to latest one
  productionImplementation 'com.midtrans:uikit:2.0.0' // change the version to latest one
  ...
}
android {
  ...
  // Define Merchant BASE URL and CLIENT KEY for each flavors
  productFlavors {
    sandbox {
      buildConfigField "String", "BASE_URL", "\"https://merchant-url-sandbox.com/\""
      buildConfigField "String", "CLIENT_KEY", "\"VT-CLIENT-sandbox-client-key\""
    }

    production {
      buildConfigField "String", "BASE_URL", "\"https://merchant-url-production.com/\""
      buildConfigField "String", "CLIENT_KEY", "\"VT-CLIENT-production-client-key\""
    }
  }
  ...
}

// Define Midtrans SDK dependencies for each flavors
dependencies {
  ...
  sandboxImplementation 'com.midtrans:uikit:1.34.0-SANDBOX' // change the version to latest one
  productionImplementation 'com.midtrans:uikit:1.34.0' // change the version to latest one
  ...
}

You can support two payment environments in your app by defining two flavors in your build.gradle.


Initialize Midtrans SDK using provided base URL and client key in BuildConfig

UiKitApi.Builder()
        .withMerchantClientKey(CLIENT_KEY) // client_key is mandatory
        .withContext(CONTEXT) // context is mandatory
        .withMerchantUrl(BASE_URL) // set transaction finish callback (sdk callback)
        .enableLog(true) // enable sdk log (optional)
        .withFontFamily(ASSET_FONT) // set font family
        .withColorTheme(CustomColorTheme("#FFE51255", "#B61548", "#FFE51255")) // set theme. it will replace theme on snap theme on MAP ( optional)
        .build()
SdkUIFlowBuilder.init()
  .setClientKey(CLIENT_KEY) // client_key is mandatory
  .setContext(CONTEXT) // context is mandatory
  .setTransactionFinishedCallback(new TransactionFinishedCallback() {
    	@Override
      public void onTransactionFinished(TransactionResult result) {
      	// Handle finished transaction here.
      }
    }) // set transaction finish callback (sdk callback)
  .setMerchantBaseUrl(BASE_URL) //set merchant url (required)
  .enableLog(true) // enable sdk log (optional)
  .setColorTheme(new CustomColorTheme("#FFE51255", "#B61548", "#FFE51255")) // set theme. it will replace theme on snap theme on MAP ( optional)
  .buildSDK();

Initialize your SDK using merchant BASE_URL and CLIENT_KEY provided by BuildConfig data.




Starting Payment


Use Snap Token Flow


The easiest way to use the SDK - what you need to do will be to integrate to Midtrans' backend and retrieve the Snap Token, then pass Snap token as argument of startPaymentUiFlow method. SDK will then show the payment page immediately; no need to create any transaction object on the SDK.



val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result?.resultCode == RESULT_OK) {
        result.data?.let {
            val transactionResult = it.getParcelableExtra<TransactionResult>(UiKitConstants.KEY_TRANSACTION_RESULT)
            Toast.makeText(this,"${transactionResult?.transactionId}", Toast.LENGTH_LONG).show()
        }
    }
}

UiKitApi.getDefaultInstance().startPaymentUiFlow(
    this@MainActivity, // Activity
    launcher, // ActivityResultLauncher
    "25e3659b-f00c-4d98-bfff-9f72978c8df5" // Snap Token
)
MidtransSDK.getInstance().startPaymentUiFlow(ACTIVITY_CONTEXT, SNAP_TOKEN);




Transaction Finished Callback


In order to get the Transaction Result, you can subscribe for a Transaction Finished Callback by overriding onTransactionFinished method. The method will provide TransactionResult object which has several states.

When response is not null:

  • TransactionResult.STATUS_SUCCESS
  • TransactionResult.STATUS_PENDING
  • TransactionResult.STATUS_FAILED

When transaction is canceled, it can be checked with result.isTransactionCanceled()

When response is null:

  • TransactionResult.STATUS_INVALID

//In revamp we use launcher instead of callback, the transaction result will send to the launcher.

val launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
    if (result?.resultCode == RESULT_OK) {
        result.data?.let {
            val transactionResult = it.getParcelableExtra<TransactionResult>(UiKitConstants.KEY_TRANSACTION_RESULT)
            Toast.makeText(this,"${transactionResult?.transactionId}", Toast.LENGTH_LONG).show()
        }
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (resultCode == RESULT_OK) {
        val transactionResult = data?.getParcelableExtra<TransactionResult>(
            UiKitConstants.KEY_TRANSACTION_RESULT
        )
        if (transactionResult != null) {
            when (transactionResult.status) {
                STATUS_SUCCESS -> {
                    Toast.makeText(this, "Transaction Finished. ID: " + transactionResult.transactionId, Toast.LENGTH_LONG).show()
                }
                STATUS_PENDING -> {
                    Toast.makeText(this, "Transaction Pending. ID: " + transactionResult.transactionId, Toast.LENGTH_LONG).show()
                }
                STATUS_FAILED -> {
                    Toast.makeText(this, "Transaction Failed. ID: " + transactionResult.transactionId, Toast.LENGTH_LONG).show()
                }
                STATUS_CANCELED -> {
                    Toast.makeText(this, "Transaction Cancelled", Toast.LENGTH_LONG).show()
                }
                STATUS_INVALID -> {
                    Toast.makeText(this, "Transaction Invalid. ID: " + transactionResult.transactionId, Toast.LENGTH_LONG).show()
                }
                else -> {
                    Toast.makeText(this, "Transaction ID: " + transactionResult.transactionId + ". Message: " + transactionResult.status, Toast.LENGTH_LONG).show()
                }
            }
        } else {
            Toast.makeText(this, "Transaction Invalid", Toast.LENGTH_LONG).show()
        }
    }
    super.onActivityResult(requestCode, resultCode, data)
}
@Override
  public void onTransactionFinished(TransactionResult result) {
    if (result.getResponse() != null) {
        switch (result.getStatus()) {
          case TransactionResult.STATUS_SUCCESS:
          	Toast.makeText(this, "Transaction Finished. ID: " + result.getResponse().getTransactionId(), Toast.LENGTH_LONG).show();
          break;
          case TransactionResult.STATUS_PENDING:
          	Toast.makeText(this, "Transaction Pending. ID: " + result.getResponse().getTransactionId(), Toast.LENGTH_LONG).show();
          break;
          case TransactionResult.STATUS_FAILED:
          	Toast.makeText(this, "Transaction Failed. ID: " + result.getResponse().getTransactionId() + ". Message: " + result.getResponse().getStatusMessage(), Toast.LENGTH_LONG).show();
          break;
        }
        result.getResponse().getValidationMessages();
      } else if (result.isTransactionCanceled()) {
      	Toast.makeText(this, "Transaction Canceled", Toast.LENGTH_LONG).show();
      } else {
        if (result.getStatus().equalsIgnoreCase(TransactionResult.STATUS_INVALID)) {
        	Toast.makeText(this, "Transaction Invalid", Toast.LENGTH_LONG).show();
        } else {
        	Toast.makeText(this, "Transaction Finished with failure.", Toast.LENGTH_LONG).show();
        }
    }
  }


Logging

Enable to show SDK log for debugging. For security purposes however, we encourage merchants to disable it on production apps.

UiKitApi.Builder()
        .withMerchantClientKey(CLIENT_KEY) // client_key is mandatory
        .withContext(CONTEXT) // context is mandatory
        .withMerchantUrl(BASE_URL) // set transaction finish callback (sdk callback)
        .enableLog(true) // enable sdk log (optional)
        .withFontFamily(ASSET_FONT)
        .withColorTheme(CustomColorTheme("#FFE51255", "#B61548", "#FFE51255"))
        .build()
setLocaleNew("en") //`en` for English and `id` for Bahasa

...


Note:
Enable during initialization. 

CONTEXT: Application/activity context
CLIENT_KEY: Your Midtrans client key 
BASE_URL: Your merchant server URL
enableLog: enable to show sdk log for debugging