SDK Tap To Phone
Ecart Pay Android SDK - Tap to Phone
Accept in-person card payments in your Android app using Tap to Phone technology. Enable your users to accept contactless payments by simply tapping the card on the device — no additional hardware required.
Prerequisites
| Requirement | Minimum Version |
|---|---|
| Android SDK | minSdk 24 (Android 7.0) |
| Android Studio | Hedgehog or later |
| Device | NFC-enabled with Tap to Phone support |
Important: The SDK requires a physical device with NFC support. Testing on emulators is not possible.
Get Your Credentials
To integrate the SDK, you need a pair of API keys specifically for Tap to Phone.
Steps to obtain credentials
- Log in to Ecart Pay
- In the sidebar menu, navigate to Integrations
- Select Dev Tools
- Click on Credentials
- Create new API Keys selecting the type SDK Tap To Phone
- Securely save your
Public IDandPrivate ID
Tip: You can create separate credentials for Sandbox (testing) and Production environments.
Warning: YourPrivate IDis confidential. Never share it or include it in publicly visible code.
Installation
Add the Ecart Pay SDK to your project using Maven Central:
// build.gradle (app module)
dependencies {
implementation 'com.ecartpay:tap-to-phone-sdk:1.0.1'
}Make sure Maven Central is included in your repositories:
// settings.gradle
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}Initial Setup
Initialize the SDK once, preferably in your Application class:
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
EcartPaySDK.initialize(
context = this,
config = EcartPayConfig(
publicId = "pk_live_your_public_id",
privateId = "sk_live_your_private_id",
environment = Environment.PRODUCTION
)
)
}
}Available Environments
| Environment | Use Case | Base URL |
|---|---|---|
Environment.SANDBOX | Testing and development | sandbox.ecartpay.com |
Environment.PRODUCTION | Live transactions | ecartpay.com |
Quick Start
Set Up the Device (Recommended)
The setupDevice() method executes the entire setup process in a single call:
- Registers the device with Ecart Pay servers
- Enrolls the device with the payment provider (CyberSource)
EcartPaySDK.instance().setupDevice(activity = this) { result ->
when (result) {
is SetupResult.Ready -> {
// ✅ Device ready to process payments
Log.d("EcartPay", "Setup complete: ${result.deviceId}")
enableChargeButton()
}
is SetupResult.TapToPayReadyRequired -> {
// ⚠️ "Tap to Pay Ready" app installation required
showInstallationDialog()
}
is SetupResult.Cancelled -> {
// ℹ️ User cancelled the process
Log.i("EcartPay", "Setup cancelled")
}
is SetupResult.RegistrationFailed -> {
// ❌ Registration error
Log.e("EcartPay", "Error: ${result.code} - ${result.message}")
showError("Could not register device")
}
is SetupResult.EnrollmentFailed -> {
// ❌ Registration succeeded but enrollment failed
Log.e("EcartPay", "Enrollment error: ${result.message}")
showError("Could not complete setup")
}
}
}Install Tap to Pay Ready
When the SDK reports TapToPayReadyRequired, prompt the user to install the Visa companion app:
private fun showInstallationDialog() {
AlertDialog.Builder(this)
.setTitle("Installation Required")
.setMessage("To accept card payments, you need to install Visa's \"Tap to Pay Ready\" application.")
.setPositiveButton("Install") { _, _ ->
EcartPaySDK.instance().openTapToPayReadyPlayStore()
}
.setNegativeButton("Cancel", null)
.show()
}Process a Payment
Once the device is set up, you can process payments in different ways:
Option A: Direct Charge (Simple Amount)
Ideal for quick charges where you don't need itemized products:
val request = PaymentRequest(
amount = 150.00,
currency = "MXN",
email = "[email protected]",
reference = "SALE-001"
)
EcartPaySDK.instance().startPayment(activity = this, request = request) { result ->
when (result) {
is PaymentResult.Approved -> {
// ✅ Payment successful
showReceipt(
orderId = result.orderId,
transactionId = result.transactionId,
amount = result.amount,
card = result.maskedCardNumber
)
}
is PaymentResult.Declined -> {
// ❌ Payment declined
showError("Payment declined: ${result.reason}")
}
is PaymentResult.Cancelled -> {
// ℹ️ User cancelled
Log.i("EcartPay", "Payment cancelled")
}
is PaymentResult.Error -> {
// ❌ Technical error
Log.e("EcartPay", "Error: ${result.code} - ${result.message}")
showError("An error occurred while processing the payment")
}
}
}Option B: Itemized Charge
For sales with detailed products, useful for receipts and inventory:
val request = PaymentRequest(
currency = "MXN",
email = "[email protected]",
firstName = "John",
lastName = "Doe",
phone = "5512345678",
items = listOf(
OrderItem(name = "Product A", price = 99.00, quantity = 2),
OrderItem(name = "Product B", price = 150.00, quantity = 1)
)
)
EcartPaySDK.instance().startPayment(activity = this, request = request) { result ->
// Handle result...
}Option C: Complete Charge (with Shipping)
For e-commerce with physical products:
val request = PaymentRequest(
currency = "MXN",
email = "[email protected]",
firstName = "Jane",
lastName = "Smith",
phone = "5598765432",
reference = "ORDER-2024-001",
referenceId = "ORD-12345",
notifyUrl = "https://your-server.com/webhooks/ecartpay",
items = listOf(
OrderItem(name = "Smartphone XYZ", price = 8999.00, quantity = 1)
),
shippingAddress = ShippingAddress(
firstName = "Jane",
lastName = "Smith",
address1 = "123 Main Street",
address2 = "Apt 4B",
city = "Mexico City",
postalCode = "06600",
countryCode = "MX",
stateCode = "CDMX",
phone = "5598765432"
),
shippingItems = listOf(
ShippingItem(
name = "Express Shipping",
amount = 150.00,
carrier = "FEDEX"
)
)
)Field Reference
PaymentRequest
| Field | Type | Required | Description |
|---|---|---|---|
amount | Double? | When no items | Total amount to charge |
currency | String | Yes | Currency: "MXN" or "USD" |
email | String? | No | Customer email |
phone | String? | No | Customer phone |
firstName | String? | No | Customer first name |
lastName | String? | No | Customer last name |
reference | String? | No | Internal reference (e.g., invoice number) |
referenceId | String? | No | External ID from your system |
notifyUrl | String? | No | Webhook notification URL |
items | List<OrderItem>? | When no amount | List of products |
shippingItems | List<ShippingItem>? | No | Shipping options |
shippingAddress | ShippingAddress? | No | Delivery address |
OrderItem
| Field | Type | Required | Description |
|---|---|---|---|
name | String | Yes | Product name |
price | Double | Yes | Unit price (> 0) |
quantity | Int | Yes | Quantity (≥ 1) |
ShippingItem
| Field | Type | Required | Description |
|---|---|---|---|
name | String | Yes | Shipping method name |
amount | Double | Yes | Cost (can be 0) |
carrier | String | Yes | Carrier (e.g., "FEDEX", "DHL") |
trackingNumber | String? | No | Tracking number |
ShippingAddress
| Field | Type | Required | Description |
|---|---|---|---|
firstName | String? | Yes* | Recipient first name |
lastName | String? | No | Recipient last name |
address1 | String? | Yes* | Primary address |
address2 | String? | No | Secondary address |
address3 | String? | No | Additional address |
city | String? | No | City |
postalCode | String? | No | Postal code |
countryCode | String? | Yes* | ISO country code (e.g., "MX") |
countryName | String? | No | Country name |
stateCode | String? | No | State code |
stateName | String? | No | State name |
phone | String? | No | Contact phone |
reference | String? | No | Delivery instructions |
*Required by backend when shippingAddress is included
Advanced Integration
Step-by-Step Setup
If you need more control over the flow (e.g., to show different UI states for each step), you can use the individual methods:
Step 1: Register the Device
EcartPaySDK.instance().registerDevice { result ->
when (result) {
is RegistrationResult.Success -> {
Log.d("EcartPay", "Device registered: ${result.deviceId}")
// Continue with enrollment
}
is RegistrationResult.Failure -> {
Log.e("EcartPay", "Error: ${result.code} - ${result.message}")
}
}
}Step 2: Enroll the Device
EcartPaySDK.instance().enrollDevice(activity = this) { result ->
when (result) {
is EnrollmentResult.Enrolled -> {
Log.d("EcartPay", "Enrollment complete")
}
EnrollmentResult.TapToPayReadyRequired -> {
// Show installation dialog
}
EnrollmentResult.Cancelled -> {
Log.i("EcartPay", "Enrollment cancelled")
}
is EnrollmentResult.Failure -> {
Log.e("EcartPay", "Error: ${result.code} - ${result.message}")
}
}
}Check Device Status
val sdk = EcartPaySDK.instance()
// Is registered?
val isRegistered = sdk.isDeviceRegistered
val deviceId = sdk.deviceId
// Has provider credentials?
val hasCredentials = sdk.hasProviderCredentials
// Is enrolled?
val isEnrolled = sdk.isEnrollmentPersisted
// Is Tap to Pay Ready installed?
val isAppInstalled = sdk.isTapToPayReadyInstalled()Reset the Device
To clear all local configuration (useful when signing out):
EcartPaySDK.instance().reset()Network Monitoring (Debug)
For debugging, you can observe all HTTP requests:
EcartPaySDK.instance().setNetworkListener(object : NetworkListener {
override fun onRequest(method: String, url: String, body: String?) {
Log.d("HTTP", "→ $method $url")
body?.let { Log.d("HTTP", it) }
}
override fun onResponse(method: String, url: String, statusCode: Int, body: String) {
Log.d("HTTP", "← $method $url [$statusCode]")
}
override fun onError(method: String, url: String, error: Throwable) {
Log.e("HTTP", "✕ $method $url - ${error.message}")
}
})
// Disable when done
EcartPaySDK.instance().setNetworkListener(null)Error Handling
| Code | Description | Solution |
|---|---|---|
NOT_REGISTERED | Device not registered | Call setupDevice() or registerDevice() |
NOT_ENROLLED | Device not enrolled | Call setupDevice() or enrollDevice() |
TAP_TO_PAY_READY_REQUIRED | Visa app not installed | Use openTapToPayReadyPlayStore() |
MISSING_PROVIDER_CREDENTIALS | Incomplete credentials | Contact Ecart Pay support |
API_401 | Invalid credentials | Verify your publicId and privateId |
API_400 | Invalid request data | Check PaymentRequest fields |
ProGuard / R8
If you use code shrinking, the SDK includes its own ProGuard rules. No additional configuration is required.
Support
Have questions or issues with integration?
- 📧 Email: [email protected]
- 📖 Documentation: docs.ecartpay.com
- 💬 Live Chat: Available in the Ecart Pay dashboard
Updated about 7 hours ago