Android Htech - Movil
SDK Htech para aplicaciones Android.
Requisitos
| Nombre | Valor |
|---|---|
| minSdk | 23 (Android 6.0) |
| compileSdk recomendado | 35 o superior. |
| Java / JVM target | 11 |
| Version Kotlin | 2.1.0 o superior |
| UI | Jetpack Compose (Material 3) |
| Permisos | INTERNET |
Antes de usar el SDK tu backend debe generar un orderId con el proceso begin transaction (para pagos) o begin-transaction-create-card (para guardar tarjeta). El SDK nunca crea órdenes: solo captura la tarjeta y procesa la orden que le indiques.
Nunca generes el orderId desde la app ni guardes llaves/credenciales de Htech en el código del cliente.
Instalacion.
Asegúrate de tener mavenCentral() en tus repositorios (settings.gradle.kts):
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}
Si utilizas Kotlin DSL:
dependencies {
implementation("com.htech:sdk:1.0.0")
}Si utilizas Groovy:
dependencies {
implementation 'com.htech:sdk:1.0.0'
}Si utilizas el Version catalog (toml):
[versions]
htechSdk = "1.0.0"
[libraries]
htech-sdk = { group = "com.htech", name = "sdk", version.ref = "htechSdk" }Ambientes: Sandbox y Produccion.
El SDK está en modo Sandbox por default, lo que te permite hacer transacciones de prueba antes de pasar a producción. Para cobros reales crea la instancia con isSandboxMode = false.
El orderId debe pertenecer al mismo ambiente que el SDK. Una orden creada en sandbox no funcionará con isSandboxMode = false y viceversa.
RecomendacionControla el ambiente con BuildConfig para no publicar por error una versión apuntando a sandbox:
val htech by lazy { Htech(isSandboxMode = BuildConfig.DEBUG) }Integracion.
El SDK ofrece dos formas de integrarse. Elige según cuánto control necesitas sobre la UI:
| Pantalla Completa | Widget | |
|---|---|---|
| UI | El SDK abre su propia pantalla | Se inserta dentro de tu pantalla Compose |
| Esfuerzo de integración | Mínimo (una llamada suspend) | Medio (controller + callbacks) |
| Control del botón "Pagar" | Del SDK | Tuyo (controller.processPayment()) |
| Requiere Compose | No (solo una ComponentActivity) | Sí |
| 3D Secure | Automático | Automático (el widget pasa a pantalla completa) |
Integración en pantalla completa
Crear la instancia.
import com.htech.sdk.Htech
val htech by lazy { Htech() } // Sandbox
val htech by lazy { Htech(isSandboxMode = false) } // Producción
Pago con tarjeta nueva - makePayment
Todas las funciones son suspend: llámalas desde una corrutina (lifecycleScope, viewModelScope o rememberCoroutineScope() en Compose). Reciben una ComponentActivity (por ejemplo AppCompatActivity o ComponentActivity) para abrir la pantalla de pago y devuelven el resultado cuando la pantalla se cierra.
lifecycleScope.launch {
val response = htech.makePayment(
orderId = orderId, // obtenido de tu backend (begin transaction)
activity = this@MainActivity,
isDarkTheme = false, // opcional, default false
)
if (response.isSuccess) {
val authorization = response.payload["authorizationNumber"] as? String
// Mostrar confirmación
} else {
val reason = response.payload["responseDescription"] as? String
// Mostrar error / permitir reintento
}
}
Pago con tarjeta guardada - makePaymentByToken
Muestra la pantalla de pago con la tarjeta tokenizada asociada a la orden (el usuario confirma el CVV).
lifecycleScope.launch {
val response = htech.makePaymentByToken(
orderId = orderId,
activity = this@MainActivity,
isDarkTheme = false, // requerido en esta función
)
}
Guardar tarjeta - saveCard
Requiere un orderId generado con begin-transaction-create-card.
lifecycleScope.launch {
val result = htech.saveCard(
orderId = orderId,
activity = this@MainActivity,
isDarkTheme = false,
)
if (result.isSuccess) {
// Tarjeta guardada
} else {
Log.w("Htech", "No se guardó la tarjeta: ${result.message}")
}
}Integración con widget embebido
Composable que renderiza los campos de tarjeta dentro de tu propia pantalla. Tú controlas cuándo validar y cuándo cobrar mediante un HtechFieldController.
Ejemplo:
import com.htech.sdk.HtechCardFieldType
import com.htech.sdk.HtechCardFieldWidget
import com.htech.sdk.HtechFieldController
import com.htech.sdk.HtechWidgetStatus
@Composable
fun CheckoutScreen(orderId: String) {
val controller = remember { HtechFieldController() }
var isFormValid by remember { mutableStateOf(false) }
var isProcessing by remember { mutableStateOf(false) }
Column(Modifier.padding(16.dp)) {
HtechCardFieldWidget(
orderId = orderId,
controller = controller,
type = HtechCardFieldType.WIDGET,
isSandboxMode = true,
isDarkTheme = isSystemInDarkTheme(),
onValidForm = { isValid, errors ->
isFormValid = isValid
// errors.numCard / errors.date / errors.cvv == true indica campo con error
},
onProcessPayment = { isProcessing = true },
onPaymentResult = { response ->
isProcessing = false
if (response.isSuccess) { /* éxito */ } else { /* rechazo */ }
},
onError = { message ->
isProcessing = false
// Error de configuración, orderId inválido o WebView desactualizado
},
on3dsChallengeOpen = { isOpen -> /* opcional: analytics / UI */ },
onGetWidgetStatus = { status ->
// START_LOADING / FINISH_LOADING: útil para mostrar un skeleton
},
)
Button(
enabled = isFormValid && !isProcessing,
onClick = { controller.processPayment() },
) { Text("Pagar") }
}
}
Parametros:
| Parametro | Tipo | Requerido | Descripcion |
|---|---|---|---|
| orderId | String | No* | Orden a cobrar. *Si no lo envías aquí, debes enviarlo en processPayment(orderId). |
| controller | HtechFieldController | Si | Permite disparar validación y pago. |
| type | HtechCardFieldType | Si | Tipo de vista. |
| isSandboxMode | Boolean | No | Default true. Usa false en producción. |
| isDarkTheme | Boolean | No | Default false. |
| onPaymentResult | (HtechResponse) -> Unit | Si | Resultado final del pago. |
| onValidForm | (Boolean, HtechFormValid) -> Unit | Si | Estado de validación del formulario. |
| onError | (String) -> Unit | Si | Errores de integración/configuración |
| onProcessPayment | (() -> Unit)? | No | El pago comenzó a procesarse. |
| on3dsChallengeOpen | ((Boolean) -> Unit)? | No | Se abrió ( true) o cerró (false) el reto 3DS. |
| onGetWidgetStatus | ((HtechWidgetStatus) -> Unit)? | No | El usuario cerró el formulario |
| modifier | Modifier | No | El ancho es fillMaxWidth; la altura la define el type. |
Tipo de widget.
| Tipo | Uso |
|---|---|
| WIDGET | Campos de tarjeta nueva, compacto |
| PAYMENT_TOKEN_WIDGET | Pago con tarjeta guardada o token y la UI de tipo CardField. |
| CHECKOUT | Formulario de tarjeta nueva completo |
| CHECKOUT_PAYMENT_TOKEN | Formulario de tarjeta guardada completo |
| PAYMENT_TOKEN | Vista completa de pago con token, con su propio botón |
HtechFieldController.
| Metodo/Propiedad | Descripcion |
|---|---|
| processPayment(orderId: String? = null) | Inicia el pago. La respuesta llega por onPaymentResult. |
| validateForm() | Fuerza la validación. La respuesta llega por onValidForm. |
| dispose() | Libera el controller. Solo cuando el widget ya no está en pantalla. |
| isAttached | true si está vinculado a un widget montado. |
| isDisposed | true después de dispose(). |
Notas:
- Un controller solo puede estar vinculado a un widget a la vez.
- No uses un controller después de dispose(); lanza IllegalStateException.
- No llames dispose() mientras el widget está montado.
- Cuando el banco solicita autenticación 3DS, el widget pasa automáticamente a pantalla completa y regresa a su lugar al terminar. Durante el reto el botón "atrás" está bloqueado. No necesitas hacer nada adicional; on3dsChallengeOpen es solo informativo.
Respuesta - HtechResponse.
data class HtechResponse(
val isSuccess: Boolean,
val payload: Map<String, Any?>
)Estructura de payload:
| Atributo | Tipo | Descripcion |
|---|---|---|
| responseCode | String | Código de respuesta del procesador. |
| responseDescription | String | Descripción legible del resultado/rechazo. |
| authorizationNumber | String | Número de autorización (solo pagos aprobados). |
| referenceNumber | String | Referencia de la transacción. |
| status | String | "approved" o "rejected". |
| order | Map | id, amount, currency. |
| card | Map | token de la tarjeta. |
Si el usuario cierra la pantalla o hay un error de configuración, payload puede venir vacío. Usa siempre accesos seguros (as?).
Updated 26 days ago
