Android SDK
Getting started with Android SDK for Number
The EasyPay Android SDK offers access to the Number API for seamless integration with any and all Android applications. For iOS integration, refer to the iOS SDK integration guide.
Installation
Requirements
Android 6.0 (API level 23) and above
Gradle 8.2 and above
Android Gradle Plugin 8.2.1
Kotlin 1.9.22 and above
Configuration
Add easypay
to your dependencies in the build.gradle
file.
dependencies {
implementation 'com.easypaysolutions:easypay:1.1.5'
// If you want to use widgets, add the following line
implementation 'com.easypaysolutions:easypay-widgets:1.1.5'
}
Get started
Integration
Prerequisites - get API key, HMAC secret and optional Sentry DSN from Number.
During initialization, the RSA certificate download begins. Proceeding with any call before downloading has finished will result with an exception RSA_CERTIFICATE_NOT_FETCHED
.
You can check the download status by accessing the following enum:
EasyPayConfiguration.getInstance().getRsaCertificateFetchingStatus()
Using widgets
PaymentSheet
Number's prebuilt payment UI component that allows you to collect credit card information in a secure way and process payments.
Initialize a PaymentSheet
inside onCreate
of your checkout Fragment
or Activity
, passing a method to handle the payment result.
import com.easypaysolutions.payment_sheet.PaymentSheet
import com.easypaysolutions.payment_sheet.utils.PaymentSheetResult
class PaymentSheetFragment : Fragment() {
private lateinit var paymentSheet: PaymentSheet
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
paymentSheet = PaymentSheet(this, ::onPaymentSheetResult)
}
private fun onPaymentSheetResult(paymentSheetResult: PaymentSheetResult) {
// implemented in the next steps
}
}
When the customer taps the payment button, call present
method on the PaymentSheet
instance with your configuration. PaymentSheet.Configuration
requires the following parameters:
AmountsParam
- total $ amount of the payment;ConsentCreatorParam
- annual consent details, that contains the following:limitLifeTime
- the maximum $ amount that can be charged in total,limitPerCharge
- the maximum $ amount that can be charged per transaction,merchantId
- the ID of the merchant that the consent is created for,startDate
- the date when the consent is created,customerReferenceId
orconsentId
to identify the customer.
Other parameters are optional.
// ...
import com.easypaysolutions.repositories.annual_consent.create.ConsentCreatorParam
import com.easypaysolutions.repositories.charge_cc.AmountsParam
class PaymentSheetFragment : Fragment() {
// ...
private fun presentPaymentSheet() {
val totalAmount: Double = 1000.0
val consentCreator = ConsentCreatorParam(
limitLifeTime = 100000.0,
limitPerCharge = 1000.0,
merchantId = 1,
startDate = Date(),
customerReferenceId = "CUSTOMER_REFERENCE_ID"
)
val config = PaymentSheet.Configuration
.Builder()
.setAmounts(AmountsParam(totalAmount))
.setConsentCreator(consentCreator)
.build()
paymentSheet.present(config)
}
}
Handle the payment result in the onPaymentSheetResult
method.
// ...
class PaymentSheetFragment : Fragment() {
// ...
private fun onPaymentSheetResult(paymentSheetResult: PaymentSheetResult) {
when (paymentSheetResult) {
is PaymentSheetResult.Failed -> {
// Handle failure
}
is PaymentSheetResult.Completed -> {
// Handle successful payment
}
is PaymentSheetResult.Canceled -> {
// Handle cancellation
}
}
}
}
CustomerSheet
Number's prebuilt UI component that lets your customers manage their saved credit cards.
Initialize a CustomerSheet
inside onCreate
of your checkout Fragment
or Activity
, passing a method to handle the customer sheet result.
import com.easypaysolutions.customer_sheet.CustomerSheet
import com.easypaysolutions.customer_sheet.utils.CustomerSheetResult
class CustomerSheetFragment : Fragment() {
private lateinit var customerSheet: CustomerSheet
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
customerSheet = CustomerSheet(this, ::onCustomerSheetResult)
}
private fun onCustomerSheetResult(customerSheetResult: CustomerSheetResult) {
// implemented in the next steps
}
}
To present the customer sheet, call the present
method on the CustomerSheet
instance, passing your configuration. CustomerSheet.Configuration
requires the following parameters:
ConsentCreatorParam
- annual consent details, that contains the following:limitLifeTime
- the maximum $ amount that can be charged in total,limitPerCharge
- the maximum $ amount that can be charged per transaction,merchantId
- the ID of the merchant that the consent is created for,startDate
- the date when the consent is created,customerReferenceId
orconsentId
to identify the customer.
Other parameters are optional.
// ...
import com.easypaysolutions.repositories.annual_consent.create.ConsentCreatorParam
class CustomerSheetFragment : Fragment() {
// ...
private fun presentCustomerSheet() {
val consentCreator = ConsentCreatorParam(
limitLifeTime = 100000.0,
limitPerCharge = 1000.0,
merchantId = 1,
startDate = Date(),
customerReferenceId = "CUSTOMER_REFERENCE_ID"
)
val config = CustomerSheet.Configuration
.Builder()
.setConsentCreator(consentCreator)
.build()
customerSheet.present(config)
}
}
Handle the customer sheet result in the onCustomerSheetResult
method.
// ...
class CustomerSheetFragment : Fragment() {
// ...
private fun onCustomerSheetResult(customerSheetResult: CustomerSheetResult) {
when (customerSheetResult) {
is CustomerSheetResult.Failed -> {
// Handle failure
}
is CustomerSheetResult.Selected -> {
// Handle selected card - customerSheetResult.annualConsentId
}
}
}
}
Screenshots
Save Card


Manage Cards

Store and Pay

Common components
SecureTextField component
The SDK's widgets use a component called SecureTextField
which ensures a safe input of credit card number. It is a subclass of TextInputEditText
which enables freedom of styling as needed.
SecureTextField
supports only XML layout configuration:
<com.easypaysolutions.utils.secured.SecureTextField
... />
To get the SecureData
from the SecureTextField
, use the following property:
val secureData = secureTextField.secureData
Common objects
Below you'll find code describing some of the objects that are commonly used in requests or responses. You can use it as a reference. The code includes parameter names, types, and some validation rules.
SecureData
SecureData
Most commonly used as SecureData<String>
coming from the SecureTextField component.
data class SecureData<T> internal constructor(
val data: T,
)
CreditCardInfoParam
CreditCardInfoParam
data class CreditCardInfoParam(
@ValidateNumberGreaterThanZero
@ValidateLength(maxLength = 2)
val expMonth: Int,
@ValidateNumberGreaterThanZero
@ValidateLength(maxLength = 4)
val expYear: Int,
@ValidateLength(maxLength = 4)
@ValidateNotBlank
val csv: String,
)
AccountHolderDataParam
AccountHolderDataParam
data class AccountHolderDataParam(
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = FIRST_OR_LAST_NAME)
val firstName: String? = null,
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = FIRST_OR_LAST_NAME)
val lastName: String? = null,
@ValidateLength(maxLength = 100)
@ValidateRegex(regex = COMPANY)
val company: String? = null,
val billingAddress: AccountHolderBillingAddressParam,
@ValidateLength(maxLength = 150)
@ValidateRegex(regex = EMAIL)
val email: String? = null,
@ValidateLength(maxLength = 16)
@ValidateRegex(regex = ONLY_NUMBERS)
val phone: String? = null,
)
AccountHolderBillingAddressParam
AccountHolderBillingAddressParam
data class AccountHolderBillingAddressParam(
@ValidateLength(maxLength = 100)
@ValidateRegex(regex = ADDRESS1)
@ValidateNotBlank
val address1: String,
@ValidateLength(maxLength = 100)
@ValidateRegex(regex = ADDRESS2)
val address2: String? = null,
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = CITY)
val city: String? = null,
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = COUNTRY_OR_STATE)
val state: String? = null,
@ValidateLength(maxLength = 20)
@ValidateRegex(regex = ZIP_CODE)
@ValidateNotBlank
val zip: String,
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = COUNTRY_OR_STATE)
val country: String? = null,
)
EndCustomerDataParam
EndCustomerDataParam
data class EndCustomerDataParam(
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = FIRST_OR_LAST_NAME)
val firstName: String? = null,
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = FIRST_OR_LAST_NAME)
val lastName: String? = null,
@ValidateLength(maxLength = 100)
@ValidateRegex(regex = COMPANY)
val company: String? = null,
val billingAddress: EndCustomerBillingAddressParam,
@ValidateLength(maxLength = 150)
@ValidateRegex(regex = EMAIL)
val email: String? = null,
@ValidateLength(maxLength = 16)
@ValidateRegex(regex = ONLY_NUMBERS)
val phone: String? = null,
)
EndCustomerBillingAddressParam
data class EndCustomerBillingAddressParam(
@ValidateLength(maxLength = 100)
@ValidateRegex(regex = ADDRESS1)
val address1: String,
@ValidateLength(maxLength = 100)
@ValidateRegex(regex = ADDRESS2)
val address2: String? = null,
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = CITY)
val city: String? = null,
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = COUNTRY_OR_STATE)
val state: String? = null,
@ValidateLength(maxLength = 20)
@ValidateRegex(regex = ZIP_CODE)
val zip: String,
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = COUNTRY_OR_STATE)
val country: String? = null,
)
AmountsParam
AmountsParam
data class AmountsParam(
@ValidateNumberGreaterThanZero
val totalAmount: Double,
val salesTax: Double? = null,
val surcharge: Double? = null,
)
PurchaseItemsParam
data class PurchaseItemsParam(
@ValidateLength(maxLength = 200)
@ValidateRegex(regex = SERVICE_DESCRIPTION)
val serviceDescription: String? = null,
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = CLIENT_REF_ID_OR_RPGUID)
val clientRefId: String? = null,
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = CLIENT_REF_ID_OR_RPGUID)
val rpguid: String? = null,
)
ConsentCreatorParam
ConsentCreatorParam
data class ConsentCreatorParam(
val merchantId: Int,
@ValidateLength(maxLength = 200)
@ValidateRegex(regex = RegexPattern.SERVICE_DESCRIPTION)
val serviceDescription: String? = null,
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = RegexPattern.CLIENT_REF_ID_OR_RPGUID)
val customerReferenceId: String? = null,
@ValidateLength(maxLength = 75)
@ValidateRegex(regex = RegexPattern.CLIENT_REF_ID_OR_RPGUID)
val rpguid: String? = null,
val startDate: Date,
@ValidateNumberGreaterThanZero
val limitPerCharge: Double,
@ValidateNumberGreaterThanZero
val limitLifeTime: Double,
)
Public methods in the Android SDK
1. Charge credit card
This method processes a credit card when the credit card details are entered manually. Details include the card number, expiration date, CVV, card holder name and address.
ChargeCreditCard().chargeCreditCard(params: ChargeCreditCardBodyParams): NetworkResource<ChargeCreditCardResult>
REST API equivalent: Process a Card Sale
Request parameters
ChargeCreditCardBodyParams
encryptedCardNumber
: SecureData<String>creditCardInfo
: CreditCardInfoParamaccountHolder
: AccountHolderDataParamendCustomer
: EndCustomerDataParam?amounts
: AmountsParampurchaseItems
: PurchaseItemsParammerchantId
: Int
Response body
The response body will be serialized to ChargeCreditCardResult
.
data class ChargeCreditCardResult internal constructor(
@SerializedName("FunctionOk")
override val functionOk: Boolean,
@SerializedName("ErrCode")
override val errorCode: Int,
@SerializedName("ErrMsg")
override val errorMessage: String,
@SerializedName("RespMsg")
override val responseMessage: String,
@SerializedName("TxApproved")
override val txApproved: Boolean,
@SerializedName("TxID")
override val txId: Int,
@SerializedName("TxnCode")
override val txCode: String,
@SerializedName("AVSresult")
val avsResult: String,
@SerializedName("AcquirerResponseEMV")
val acquirerResponseEmv: String?,
@SerializedName("CVVresult")
val cvvResult: String,
@SerializedName("IsPartialApproval")
val isPartialApproval: Boolean,
@SerializedName("RequiresVoiceAuth")
val requiresVoiceAuth: Boolean,
@SerializedName("ResponseApprovedAmount")
val responseApprovedAmount: Double,
@SerializedName("ResponseAuthorizedAmount")
val responseAuthorizedAmount: Double,
@SerializedName("ResponseBalanceAmount")
val responseBalanceAmount: Double,
)
2. List annual consents
A query that returns annual consent details. Depending on the query sent, a single consent or multiple consents may be returned.
ListAnnualConsents().listAnnualConsents(params: ListAnnualConsentsBodyParams): NetworkResource<ListAnnualConsentsResult>
REST API equivalent: Query
Request parameters
ListAnnualConsentsBodyParams
merchantId
: Int?customerReferenceId
: String?rpguid
: String?
Either
customerReferenceId
orrpguid
must be provided to get the list of consents of a specific customer.
Response body
The response body will be serialized to ListAnnualConsentsResult
.
data class ListAnnualConsentsResult internal constructor(
@SerializedName("FunctionOk")
override val functionOk: Boolean,
@SerializedName("ErrCode")
override val errorCode: Int,
@SerializedName("ErrMsg")
override val errorMessage: String,
@SerializedName("RespMsg")
override val responseMessage: String,
@SerializedName("NumRecords")
val numRecords: Int,
@SerializedName("Consents")
val consents: List<AnnualConsent>,
)
And the AnnualConsent
looks like the following:
data class AnnualConsent internal constructor(
@SerializedName("AcctHolderFirstName")
val accountHolderFirstName: String,
@SerializedName("AcctHolderID")
val accountHolderId: Int,
@SerializedName("AcctHolderLastName")
val accountHolderLastName: String,
@SerializedName("AcctNo")
val accountNumber: String,
@SerializedName("AuthTxID")
val authTxId: Int,
@SerializedName("CreatedBy")
val createdBy: String,
@SerializedName("CreatedOn")
var createdOn: String,
@SerializedName("CustID")
val customerId: Int,
@SerializedName("CustomerRefID")
val customerReferenceId: String,
@SerializedName("EndDate")
var endDate: String,
@SerializedName("ID")
val id: Int,
@SerializedName("IsEnabled")
val isEnabled: Boolean,
@SerializedName("LimitLifeTime")
val limitLifeTime: Double,
@SerializedName("LimitPerCharge")
val limitPerCharge: Double,
@SerializedName("MerchID")
val merchId: Int,
@SerializedName("NumDays")
val numDays: Int,
@SerializedName("RPGUID")
val rpguid: String,
@SerializedName("ServiceDescrip")
val serviceDescription: String,
@SerializedName("StartDate")
var startDate: String,
)
3. Create annual consent
This method creates an annual consent by sending the credit card details, which include: card number, expiration date, CVV, and card holder contact data. It is not created by swiping the card through a reader device.
CreateAnnualConsent().createAnnualConsent(params: CreateAnnualConsentBodyParams): NetworkResource<CreateAnnualConsentResult>
REST API equivalent: Create Annual Consent
Request parameters
CreateAnnualConsentBodyParams
encryptedCardNumber
: SecureData<String>creditCardInfo
: CreditCardInfoParamaccountHolder
: AccountHolderDataParamendCustomer
: EndCustomerDataParam?consentCreator
: ConsentCreatorParam
Response body
The response body will be serialized to CreateAnnualConsentResult
.
data class CreateAnnualConsentResult internal constructor(
@SerializedName("FunctionOk")
override val functionOk: Boolean,
@SerializedName("ErrCode")
override val errorCode: Int,
@SerializedName("ErrMsg")
override val errorMessage: String,
@SerializedName("RespMsg")
override val responseMessage: String,
@SerializedName("ConsentID")
val consentId: Int,
@SerializedName("CreationSuccess")
val creationSuccess: Boolean,
@SerializedName("PreConsentAuthMessage")
val preConsentAuthMessage: String,
@SerializedName("PreConsentAuthSuccess")
val preConsentAuthSuccess: Boolean,
@SerializedName("PreConsentAuthTxID")
val preConsentAuthTxId: Int,
)
4. Cancel annual consent
Cancels an annual consent. Credit card data is removed from the system after the cancellation is complete.
CancelAnnualConsent().cancelAnnualConsent(params: CancelAnnualConsentBodyParams): NetworkResource<CancelAnnualConsentResult>
REST API equivalent: Consent Annual
Request parameters
CancelAnnualConsentBodyParams
consentId
: Int
Response body
The response body will be serialized to CancelAnnualConsentResult
.
data class CancelAnnualConsentResult internal constructor(
@SerializedName("FunctionOk")
override val functionOk: Boolean,
@SerializedName("ErrCode")
override val errorCode: Int,
@SerializedName("ErrMsg")
override val errorMessage: String,
@SerializedName("RespMsg")
override val responseMessage: String,
@SerializedName("CancelSuccess")
val cancelSuccess: Boolean,
@SerializedName("CancelledConsentID")
val cancelledConsentId: Int,
)
5. Process payment for an annual consent
This method uses the credit card stored on file to process a payment for an existing consent.
ProcessPaymentAnnual().processPaymentAnnual(params: ProcessPaymentAnnualBodyParams): NetworkResource<ProcessPaymentAnnualResult>
REST API equivalent: Consent Annual
Request parameters
ProcessPaymentAnnualBodyParams
consentId
: Int
Response body
The response body will be serialized to ProcessPaymentAnnualResult
.
data class ProcessPaymentAnnualResult internal constructor(
@SerializedName("FunctionOk")
override val functionOk: Boolean,
@SerializedName("ErrCode")
override val errorCode: Int,
@SerializedName("ErrMsg")
override val errorMessage: String,
@SerializedName("RespMsg")
override val responseMessage: String,
@SerializedName("TxApproved")
override val txApproved: Boolean,
@SerializedName("TxID")
override val txId: Int,
@SerializedName("TxnCode")
override val txCode: String,
@SerializedName("AVSresult")
val avsResult: String,
@SerializedName("AcquirerResponseEMV")
val acquirerResponseEmv: String?,
@SerializedName("CVVresult")
val cvvResult: String,
@SerializedName("IsPartialApproval")
val isPartialApproval: Boolean,
@SerializedName("RequiresVoiceAuth")
val requiresVoiceAuth: Boolean,
@SerializedName("ResponseApprovedAmount")
val responseApprovedAmount: Double,
@SerializedName("ResponseAuthorizedAmount")
val responseAuthorizedAmount: Double,
@SerializedName("ResponseBalanceAmount")
val responseBalanceAmount: Double,
)
How to properly consume the API response
All requests are suspended functions, so they should be called from coroutine scope. The result of the request is wrapped in a NetworkResource
object, which can be handled in the following way:
viewModelScope.launch {
// Example of suspended function call
val result = ChargeCreditCard().chargeCreditCard(params)
when (result) {
is NetworkResource.Status.SUCCESS -> {
// Handle success
}
is NetworkResource.Status.ERROR -> {
// Handle error
}
is NetworkResource.Status.DECLINED -> {
// Handle declined
}
}
}
Possible exceptions
EasyPaySdkException
Exceptions that are thrown by the SDK.
EASY_PAY_CONFIGURATION_NOT_INITIALIZED
Check if EasyPay.init(...)
method has been called.
MISSED_SESSION_KEY
Check if correct SESSION_KEY
has been provided in the EasyPay.init(...)
method.
MISSED_HMAC_SECRET
Check if correct HMAC_SECRET
has been provided in the EasyPay.init(...)
method.
RSA_CERTIFICATE_NOT_FETCHED
RSA certificate might not be fetched yet. Check the status by calling the EasyPayConfiguration.getInstance().getRsaCertificateFetchingStatus()
method.
RSA_CERTIFICATE_FETCH_FAILED
Contact Number.
RSA_CERTIFICATE_PARSING_ERROR
Contact Number.
EasyPayApiException
Exceptions that are thrown by the Number API.
Semantic versioning
The SDK follows semantic versioning with a three-part version number: MAJOR
.MINOR
.PATCH
.
MAJOR
version is incremented when there are incompatible API changes,MINOR
version is incremented when functionality is added in a backwards-compatible manner,PATCH
version is incremented when there are backwards-compatible bug fixes.
Feature flags
Rooted device detection
To enable rooted device detection, call the following method before calling EasyPay.init(...)
:
EasyPayFeatureFlagManager.setRootedDeviceDetectionEnabled(true)
Last updated
Was this helpful?