API Referencia
undefined

smartPOS

SDK de integración con la app de pago

El sdk de integración entrega una forma simple de interactuar con la aplicación de pago(transbank pagos) que estará instalado en los POS Android. Este sdk implementa métodos que ayudan a hacer las llamadas a la transbank pagos de manera simple, segura y además entrega la forma de interpretar las respuestas de la misma.

Mas adelante se detalla la forma de utilizar este sdk.

Requerimientos mínimos

Consideraciones

  • Todas las aplicaciones que utilicen este sdk deben pasar por un proceso de certificación que garantice su buen uso.

  • El POS no cuenta con play services.

Instalación

Por el momento la instalación se realiza usando un archivo aar.
Descarga la última versión pagoappsdk-release.aar

  1. Agrega el archivo aar:
    • Haz clic en File > New > New Module.
    • Haz clic en Import .JAR/.AAR Package y, luego, en Next.
    • Ingresa la ubicación del archivo aar. Luego, haz clic en Finish.
  2. Esto creará un archivo build.gradle para el archivo aar con el siguiente contenido:
    configurations.maybeCreate("default")
    artifacts.add("default", file('pagoappsdk.aar'))
  3. En el archivo settings.gradle agrega lo siguiente:

  4. En el archivosetting.gradle de la app, agregar en la sección de dependencias lo siguiente:

    implementation project(':pagoappsdk')

Más información se puede encontrar en la documentación de google.

El sdk cuenta con as siguientes funciones principales que son las que están disponibles para comunicarse con la app-pago.

  • Obtener totales.
  • Imprimir último voucher
  • Hacer un cierre de terminal
  • Anular una venta
  • Realizar carga de llaves
  • Hacer una venta
  • Realizar una impresión

Primero

Para ocupar el sdk se necesita obtener una apiKey. Esta se obtendrá una vez la aplicación de valor agregado sea creada en la tienda de transbank.

Una vez obtenida la apiKey de la aplicación de valor agregado, deberán solicitar al administrador de PAXSTORE que la agregue a la lista blanca.

Siempre para ocupar el sdk se crea un objeto del tipo SdkActivityLauncher

private SdkActivityLauncher sdkActivityLauncher = new SdkActivityLauncher();

Con este objeto podemos hacer todas las llamadas. Para que el sdk haga las llamadas a la app de pago necesita saber cual es el flavor en que esta compilada la aplicación de pago.

Las opciones del valor flavor son las siguientes:

  • dev
  • qa
  • prod

De ahora en adelante en la documentación se usará la codeiable flavor la cual tendrá este valor.

String flavor = "dev"; 

En las invocaciones a las llamadas se usa this . Esto hace referencia al contexto de una activity.

El primer método que se debe llamar es el decarga de llaves.
Luego se debe llamar al método hacer un cierre de terminal. Así el POS y la aplicación de pago estarán listos para comenzar a realizar transacciones, ver Habilitar POS para empezar a operar.

Habilitar POS para empezar a operar

Una vez implementada la librería a la aplicación de valor agregado. Se deben realizar los siguientes pasos para habilitar el POS para operar.

Carga de llaves

El primer paso es realizar una carga de llaves, ejecutando el metodo

RequestStatus status = sdkActivityLauncher.requestForLoadKeys(this, apiKey, flavor);

Cuando finalice de ejecutar la carga de llaves, le aparecerá el mensaje de éxito en la operacion. Esto nos indica que la carga de llaves se realizó con éxito, dejando el POS listo para el cierre.

Es importante que el POS esté conectado a internet para realizar la carga de llaves.

Cierre

Luego de haber realizado la carga de llaves, tenemos que realizar el cierre de terminal. En donde ejecutamos este metodo.

RequestStatus status = sdkActivityLauncher.requestForCloseTerminal(this, apiKey, flavor); 

En donde this corresponde al AppCompatActivity, apiKey corresponde al API KEY de su aplicación que está implementando librería y de que es el flavor de la aplicación de valor agregado

Al momento de ejecutar cierre, le aparecerá un mensaje que le pregunta si quiere imprimir el detalle de ventas, entonces si selecciona no, pasara al siguiente paso del cierre.

En caso de seleccionar si, se procederá a imprimir el detalle de ventas. En caso de no tener ventas registradas se mostrará un mensaje indicando que no hay ventas y luego continúa el proceso de cierre.

En este caso como es la habilitación del POS para empezar a operar. Por supuesto no hay ventas registradas por lo que este paso de preguntar si desea imprimir detalle de ventas, puede omitirlo.

Luego aparecerá en pantalla el número de ventas y el monto total de estas. Debe seleccionar en donde dice CONFIRMAR (si no selecciona ninguna opción entonces por TIME OUT le aparecerá un mensaje de operación cancelada ya que debe confirmar el cierre).

Una vez seleccionada la opción CONFIRMAR, se empieza a procesar y se imprime el detalle del cierre en donde se indican los totales por cada medio de pago.

Ahora ya podrá empezar a operar el POS para realizar ventas y las demás operaciones. Es importante que el POS esté conectado a internet para realizar el cierre.

Funciones

Obtener totales

RequestStatus status = sdkActivityLauncher.requestForTotales(this, apiKey, flavor); 

Esto llamará a la aplicación de pago para obtener los totales.

Ahora lo siguiente es tener preparada la aplicacion de valor agregado para obtener el intent de vuelta con la información solicitada. Siempre en la respuesta se deben manejar 4 casos

@Override
private void processTotalesResponse(int requestCode, int resultCode, @Nullable Intent data) {
BundleWraper totalesWrapper = sdkActivityLauncher.getTotalesFromActivityResult(requestCode, resultCode, data);
ErrorResponse errorResponse;
TotalVentasResponse totalVentasResponse;
switch (totalesWrapper.getStatus()) {
case REQUEST_OK:
//Obtiene el objeto con la respuesta totales venta
totalVentasResponse = (TotalVentasResponse)totalesWrapper.getResponse();
break;
case REQUEST_CANCELLED_WITHOUT_ERROR:
//El intent volvio con status cancelado por una razon desconocida
//En este caso se recomienda reintentar
errorResponse = (ErrorResponse)totalesWrapper.getResponse();
break;
case REQUEST_CANCELLED_WITH_ERROR:
//El intent volvio con status cancelado, pero existe una descripcion del error;
//Los errores estan enumerados en la clase ErrorResponseCodes errorResponse=(ErrorResponse)totalesWrapper.getResponse();
break;
case REQUEST_ERROR:
//La llamada fallo por una razon desconocida
//En este caso se recomienda reintentar
errorResponse= (ErrorResponse)totalesWrapper.getResponse();
break;
default:
break;
}
}

Imprimir último voucher

RequestStatus status = sdkActivityLauncher.requestForRePrint(this, apiKey, flavor); 

Manejamos la respuesta:

@Override
private void processReprintVoucherResponse(int requestCode, int resultCode, @Nullable Intent data) {
BundleWraper voucherWrapper = sdkActivityLauncher.getReprintResponseFromActivityResult(requestCode, resultCode, data);
ErrorResponse errorResponse; ReprintVoucherResponse reprintVoucherResponse; switch (voucherWrapper.getStatus()) {
case REQUEST_OK:
//Obtiene el objeto con la respuesta ultimo voucher
reprintVoucherResponse = (ReprintVoucherResponse)voucherWrapper.getResponse(); break;
case REQUEST_CANCELLED_WITHOUT_ERROR:
//El intent volvio con status cancelado por una razon desconocida
//En este caso se recomienda reintentar
errorResponse = (ErrorResponse)voucherWrapper.getResponse(); break;
case REQUEST_CANCELLED_WITH_ERROR:
//El intent volvio con status cancelado, pero existe una descripcion del error //Los errores estan enumerados en la clase ErrorResponseCodes
errorResponse= (ErrorResponse)voucherWrapper.getResponse(); break;
case REQUEST_ERROR:
//La llamada fallo por una razon desconocida //En este caso se recomienda reintentar
errorResponse= (ErrorResponse)voucherWrapper.getResponse(); break;
default: break;
} }

Hacer un cierre de terminal

RequestStatus status = sdkActivityLauncher.requestForCloseTerminal(this, apiKey, flavor);

Manejamos la respuesta:

@Override
private void processCloseTerminalResponse(int requestCode, int resultCode, @Nullable Intent data) {
BundleWraper closeTerminalWrapper = sdkActivityLauncher.getCloseTerminalResponseFromActivityResult(requestCode, resultCode, data);
ErrorResponse errorResponse; ProcesoResponse procesoResponse;
switch (closeTerminalWrapper.getStatus()) {
case REQUEST_OK:
//Obtiene el objeto con la respuesta del cierre
procesoResponse = (ProcesoResponse)closeTerminalWrapper.getResponse(); break;
case REQUEST_CANCELLED_WITHOUT_ERROR:
//El intent volvio con status cancelado por una razon desconocida
//En este caso se recomienda reintentar
errorResponse = (ErrorResponse)closeTerminalWrapper.getResponse(); break;
case REQUEST_CANCELLED_WITH_ERROR:
//El intent volvio con status cancelado, pero existe una descripcion del error //Los errores estan enumerados en la clase ErrorResponseCodes errorResponse= (ErrorResponse)closeTerminalWrapper.getResponse(); break;
case REQUEST_ERROR:
//La llamada fallo por una razon desconocida
//En este caso se recomienda reintentar
errorResponse= (ErrorResponse)closeTerminalWrapper.getResponse(); break;
default: break;
} }

Anular una venta

String numAnulacion = "Numero de la operación a anular";

RequestStatus status = sdkActivityLauncher.requestForAnnulment(this, apiKey, numAnulacion, flavor);

Manejamos la respuesta:

@Override
private void processAnnulmentResponse(int requestCode, int resultCode, @Nullable Intent data) {
BundleWraper annulmentWrapper = sdkActivityLauncher.getAnnulmentResponseFromActivityResult(requestCode, resultCode, data);
ErrorResponse errorResponse; AnulacionResponse anulacionResponse; switch (annulmentWrapper.getStatus()) {
case REQUEST_OK:
//Obtiene el objeto con la respuesta de la anulación
anulacionResponse = (AnulacionResponse)annulmentWrapper.getResponse();
break;
case REQUEST_CANCELLED_WITHOUT_ERROR:
//El intent volvio con status cancelado por una razon desconocida //En este caso se recomienda reintentar
errorResponse = (ErrorResponse)annulmentWrapper.getResponse(); break;
case REQUEST_CANCELLED_WITH_ERROR:
//El intent volvio con status cancelado, pero existe una descripcion del error //Los errores estan enumerados en la clase ErrorResponseCodes errorResponse= (ErrorResponse)annulmentWrapper.getResponse();
break;
case REQUEST_ERROR:
//La llamada fallo por una razon desconocida
//En este caso se recomienda reintentar
errorResponse= (ErrorResponse)annulmentWrapper.getResponse(); break;
default: break;
} }

Realizar carga de llaves

RequestStatus status = sdkActivityLauncher.requestForLoadKeys(this, apiKey, flavor); 

Manejamos la respuesta:

@Override
private void processLoadKeysResponse(int requestCode, int resultCode, @Nullable Intent data) {
BundleWraper loadKeysWrapper = sdkActivityLauncher.getLoadKeysResponseFromActivityResult(requestCode, resultCode, data);
ErrorResponse errorResponse; ProcesoResponse procesoResponse; switch (loadKeysWrapper.getStatus()) {
case REQUEST_OK:
//Obtiene el objeto con la respuesta de la carga de llaves procesoResponse = (ProcesoResponse)loadKeysWrapper.getResponse(); break;
case REQUEST_CANCELLED_WITHOUT_ERROR:
//El intent volvio con status cancelado por una razon desconocida //En este caso se recomienda reintentar
errorResponse = (ErrorResponse)loadKeysWrapper.getResponse(); break;
case REQUEST_CANCELLED_WITH_ERROR:
//El intent volvio con status cancelado, pero existe una descripcion del error //Los errores estan enumerados en la clase ErrorResponseCodes errorResponse= (ErrorResponse)loadKeysWrapper.getResponse();
break;
case REQUEST_ERROR:
//La llamada fallo por una razon desconocida
//En este caso se recomienda reintentar
errorResponse= (ErrorResponse)loadKeysWrapper.getResponse(); break;
default: break;

} }

Hacer una venta

String amount = "Valor de la venta sin puntos ni simbolos";//ej: "3000"

RequestStatus status = sdkActivityLauncher.requestForSales(this, apiKey, totalVenta, CurrencyType.PESO, flavor);

Manejamos la respuesta:

@Override
private void processSalesResponse(int requestCode, int resultCode, @Nullable Intent data) {
BundleWraper salesWrapper = sdkActivityLauncher.getSalesResponseFromActivityResult(requestCode, resultCode, data);
ErrorResponse errorResponse; VentaResponse ventaResponse; switch (salesWrapper.getStatus()) {
case REQUEST_OK:
//Obtiene el objeto con la respuesta de la carga de venta ventaResponse = (VentaResponse)salesWrapper.getResponse();
break;
case REQUEST_CANCELLED_WITHOUT_ERROR:
//El intent volvio con status cancelado por una razon desconocida //En este caso se recomienda reintentar
errorResponse = (ErrorResponse)salesWrapper.getResponse(); break;
case REQUEST_CANCELLED_WITH_ERROR:
//El intent volvio con status cancelado, pero existe una descripcion del error //Los errores estan enumerados en la
clase ErrorResponseCodes errorResponse= (ErrorResponse)salesWrapper.getResponse();
break;
case REQUEST_ERROR:
//La llamada fallo por una razon desconocida
//En este caso se recomienda reintentar
errorResponse= (ErrorResponse)salesWrapper.getResponse(); break;
default: break;
} }

Realizar una impresión

Se envia una lista con las líneas que se desean imprimir. En el siguiente codigo se muestra un ejemplo de como imprimir un voucher.

Cada una de las líneas, tiene 5 elementos:

  • El contenido del texto
  • El tipo de fuente
  • El alineamiento
  • El estilo del texto
  • El tamaño de la fuente

Cada uno de estos valores esta descrito más abajo.

La cantidad de caracteres soportados por linea dependen del tipo de fuente, de si esta centrado o a la izquierda, del estilo del texto y del tamaño de fuente.

Aqui un ejemplo de imprimir, como se ve son codeias lineas que se van agregando con lines.add()

ArrayList<PrinterLine> lines = new ArrayList<>();
lines.add(new PrinterLine("Venta Efectivo 01/0000009", FontType.COUSINE, Alignment.CENTER, TextStyle.NORMAL, FontSize.BIG));
lines.add(new PrinterLine("-------------------------------------------", FontType.YAHEIB, Alignment.LEFT, TextStyle.NORMAL, FontSize.BIG));
lines.add(new PrinterLine("19 ene. 2021 15:54", FontType.COUSINE, Alignment.CENTER, TextStyle.NORMAL, FontSize.BIG));
lines.add(new PrinterLine("Atendido por: Admin", FontType.COUSINE, Alignment.CENTER, TextStyle.NORMAL, FontSize.NORMAL));
lines.add(new PrinterLine("-------------------------------------------", FontType.COUSINE, Alignment.LEFT, TextStyle.NORMAL, FontSize.BIG));
lines.add(new PrinterLine("Articulos:", FontType.COUSINE, Alignment.CENTER, TextStyle.NORMAL, FontSize.NORMAL));
lines.add(new PrinterLine("------------------------------------", FontType.COUSINE, Alignment.LEFT, TextStyle.NORMAL, FontSize.BIGGEST));
lines.add(new PrinterLine(" 1.000 x $50", FontType.COUSINE, Alignment.LEFT, TextStyle.NORMAL, FontSize.NORMAL));
lines.add(new PrinterLine("lata bebida $50", FontType.COUSINE, Alignment.LEFT, TextStyle.NORMAL, FontSize.BIG));
lines.add(new PrinterLine(" 1.000 x $50", FontType.COUSINE, Alignment.LEFT, TextStyle.NORMAL, FontSize.NORMAL));
lines.add(new PrinterLine("lata bebida $50", FontType.COUSINE, Alignment.LEFT, TextStyle.NORMAL, FontSize.BIG));
lines.add(new PrinterLine("-----------------------------------------------------", FontType.COUSINE, Alignment.LEFT, TextStyle.NORMAL, FontSize.NORMAL));
lines.add(new PrinterLine(" Total $100", FontType.YAHEIB, Alignment.LEFT, TextStyle.BOLD, FontSize.BIG));
RequestStatus status = sdkActivityLauncher.callPrinterWith(this, apiKey, lines, BuildConfig.FLAVOR);

Manejamos la respuesta:

@Override
private void processPrintResponse(int requestCode, int resultCode, @Nullable Intent data) {
BundleWraper printerWrapper = sdkActivityLauncher.getPrinterResponseFromActivityResult(requestCode, resultCode, data);
ErrorResponse errorResponse;
PrinterResponse printerResponse; switch (printerWrapper.getStatus()) {
case REQUEST_OK:
//Obtiene el objeto con la respuesta de la impresión printerResponse = (PrinterResponse)printerWrapper.getResponse(); break;
case REQUEST_CANCELLED_WITHOUT_ERROR:
//El intent volvio con status cancelado por una razon desconocida //En este caso se recomienda reintentar
errorResponse = (ErrorResponse)printerWrapper.getResponse(); break;
case REQUEST_CANCELLED_WITH_ERROR:
//El intent volvio con status cancelado, pero existe una descripcion del error //Los errores estan enumerados en la clase ErrorResponseCodes errorResponse= (ErrorResponse)printerWrapper.getResponse();
break;
case REQUEST_ERROR:
//La llamada fallo por una razon desconocida
//En este caso se recomienda reintentar
errorResponse= (ErrorResponse)printerWrapper.getResponse(); break;
default: break;
} }

Implementación en la PWA

Para hacer llamadas desde la PWA, se hacen de la siguiente manera usando javascript:

  • Llamar a anular transacción: AppPagoSDK.requestForAnnulment("numero de la transacción aanular");
  • Llamar a venta: AppPagoSDK.requestForSales ("monto de la transacción");
  • Pedir los totales: AppPagoSDK.requestForTotals();
  • Imprimir último voucher: AppPagoSDK.requestForReprintLastVoucher();
  • Realizar carga de llaves: AppPagoSDK.requestForLoadKeys();
  • Realizar cierre de terminal: AppPagoSDK.requestForCloseTerminal();
  • Llamar a imprimir: AppPagoSDK.callPrinterWith(JSONringify({lines:[{content:"Texto",fontType:1, alignment: 1, 1, 1}]}));

Cada uno de los objetos que se envian en la lista de lineas(lines) tienen la estructura de la clase PrinterLine usada en la sección donde se explica como imprimir . La diferencia es que en este caso se pasan los valores numericos asociados a los enums Alignment , FontType , TextStyle y FontSize

Integración con PWAs

Para realizar llamadas a la aplicación de pago desde una PWA, lo que se debe hacer es ocupar las mismas llamadas detalladas en esta documentación.

Esta sección abordará como implementar la interfaz entre el web view y el código nativo.

  • Primero necesitamos agregar un web view en el cual cargaremos nuestra pwa.
  • Inicializamos el webView(lo llamaremos payWebView). En la WebInterface, agregaremos el objeto SdkJavascriptInterface y en el implementaremos la interfaz SdkJavascriptInterface
  • En cada uno de los métodos a implementar ejecutaremos la llamada al método de la clase SdkActivityLauncher. Cada uno de estos métodos recibe el valor entregado desde las llamadas hechas por el webView como se detalla en la sección.
private WebView payWebview;
private WebSettings settings;
PwaActivity self = this;//Aquí se guarda una referencia al contexto de la activity en la que vive la PWA(PwaActivity es el nombre de la activity donde esta el webView)
payWebview = findViewById(R.id.payWebview);
settings = payWebview.getSettings();
settings.setJavaScriptEnabled(true);
payWebview.setWebViewClient(new WebViewClient());
payWebview.loadUrl(url);
WebInterface webInterface = new WebInterface(new SdkJavascriptInterface() {
@Override
public void requestForTotals() {
RequestStatus status = sdkActivityLauncher.requestForTotales(self, apiKey, flavor); }
@Override
public void requestForRePrint() {
RequestStatus status = sdkActivityLauncher.requestForRePrint(self, apiKey, flavor); }
@Override
public void requestForCloseTerminal() {
RequestStatus status = sdkActivityLauncher.requestForCloseTerminal(self, apiKey, flavor); }
@Override
public void requestForAnnulment(@NotNull String s) {
//Aquí "s" es el numero de la transacción a anular que fue pasado por el
webView
RequestStatus status = sdkActivityLauncher.requestForAnnulment(self, apiKey, s, packageAppVa, flavor);
}
@Override
public void requestForLoadKeys() {
RequestStatus status = sdkActivityLauncher.requestForLoadKeys(self, apiKey, flavor); }
@Override
public void requestForSales(@NotNull String s) {
//Aquí "s" es el valor del monto a transaccionar que fue pasado por el webView
RequestStatus status = sdkActivityLauncher.requestForSales(self, apiKey, s,
CurrencyType.PESO, packageAppVa, flavor); }
@Override
public void callPrinterWith(@NotNull ArrayList<PrinterLine> printerLines) {
sdkActivityLauncher.callPrinterWith(self, apiKey, printerLines, flavor); }
});
payWebview.addJavascriptInterface(webInterface, WebInterface.APP_PAGO_SDK_INTERFACE_NAME);//APP_PAGO_SDK_INTERFACE_NAME es el nombre del objeto que esta dispobible en el web view para interactuar con el código nativo(AppPagoSDK)

Como enviar a la PWA las respuestas de las llamadas a app de pago

Al igual que lo explicado en secciones anteriores, usamos el onActivityResult, pero en este caso, el resultado hay que pasarlo en formato JSON a la PWA(En los siguentes ejemplos se usa Gson para convertir a JSON).

Para esto podemos definir un método en la PWA llamado getResponse

function getResponse(response) {
JSON.stringify(response);
}

A este método le enviamos el JSON de respuesta de la seguiente manera. En nuestra activity agregamos un método como el siguiente al cual le pasaremos en este caso la respuesta de la llamada a obtener totales:

protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); BundleWraper totalesWrapper =
sdkActivityLauncher.getTotalesFromActivityResult(requestCode, resultCode, data); Gson gson = new GsonBuilder().setPrettyPrinting().create();
switch (totalesWrapper.getStatus()) {
case REQUEST_OK: updateResponseInWebview(gson.toJson(totalesWrapper.getResponse())); break;
case REQUEST_CANCELLED_WITHOUT_ERROR:
SimpleError simpleError = new SimpleError("Intent cancelado sin devolver error");
updateResponseInWebview(gson.toJson(simpleError)); break;
case REQUEST_CANCELLED_WITH_ERROR:
ErrorResponse errorResponse = (ErrorResponse) totalesWrapper.getResponse(); updateResponseInWebview(gson.toJson(errorResponse));
break;
case REQUEST_ERROR:
SimpleError simpleError = new SimpleError("Error procesando respuesta"); updateResponseInWebview(gson.toJson(simpleError));
break;
} }
private void updateResponseInWebview(String json) { payWebview.evaluateJavascript("javascript:getResponse(" + json + ")", value -> {
}); }

Si queremos solamente enviar un mensaje podemos usar la clase SimpleError . Esto no quiere decir que esa sea la única manera de hacerlo, lo importante es que se envíe un JSON para mantener la consistencia de los mensajes a la PWA.

Clases y su significado

Clase Descripción
TotalVentasResponse Esta clase tiene la estructura de la respuesta de obtener totales
PrinterResponse Esta clase tiene la estructura de la respuesta de imprimir. Aqui solo viene un status donde sus valores estan definidos en PrinterResponseCodes
ErrorResponse Esta clase tiene la estructura de los errores que pueden ser enviados al consultar la app de pago
ErrorResponseCodes Cuando ocurre un error, los errores posibles estan enumerados en esta clase
CurrencyType Enum que contiene los tipos de monedas soportados. En este momento solo se usa el "peso" y se ocupa en la llamada a realizar venta
AnulacionResponse Esta clase tiene la estructura de la respuesta de obtener la anualción de venta
PrinterLine Esta clase tiene las propiedades de imprimir linea como el tamaño del texto, estilo, alineación entro otras
ProcesoResponse Esta clase tiene la estructura de la respuesta de operacion correcta de las funciones
ReprintVoucherResponse Esta clase tiene la estructura de la respuesta de imprimir ultimo voucher
PrinterResponseCodes Esta clase tiene los codigos de respuesta de imprimir
VentaResponse Esta clase tiene la estructura de la respuesta de hacer venta
ResponseDisplayActivity Esta clase Activity contiene el como se muestra el resultado de la consulta de una función

Códigos de respuesta

Cuando se ejecuta una operación como realizar una venta con tarjeta de crédito por ejemplo. Se utiliza el flujo de la aplicación de pagos para realizar esta operación en donde al finalizar. La aplicación de pagos muestra el mensaje de éxito en caso de que se realizó la venta correctamente. Como también puede haber un error en la venta por ejemplo que la tarjeta utilizada no corresponde. Entonces la aplicación de pagos muestra el mensaje y un código de respuesta propio.

Al momento de enviar la respuesta desde la aplicación de pagos hacia la librería y luego hacia la aplicación de valor agregado. El código de respuesta de la operación se homologará con los códigos de respuesta de la librería.

Aquí el detalle de los códigos de respuesta que la librería enviará como respuesta hacia la aplicación de valor agregado.

Código
Descripción
0 Aprobado
1 Rechazado
3 Conexión Fallo
4 Transacción ya Fue Anulada
5 No existe Transacción para Anular
6 Tarjeta no Soportada
7 Transacción Cancelada desde el POS
8 No puede Anular Transacción Debito
9 Error Lectura Tarjeta
10 Monto menor al mínimo permitido
11 No existe venta
12 Transacción No Soportada
13 Debe ejecutar cierre
18 Menú invalido
19 ERROR_TARJ_DIST (Error tarjeta distinta)
20 Tarjeta Invalida
21 Anulación. No Permitida
22 TIMEOUT
24 Impresora Sin Papel
25 Fecha Invalida
26 Debe Cargar Llaves
27 Debe Actualizar
28 Debe enviar comercio Prestador
60 Error en Número de Cuotas
61 Error en Armado de Solicitud
65 Error al Procesar la Respuesta del Host
67 Superó Número Máximo de Ventas, Debe Ejecutar Cierre
68 Error Genérico, Falla al Ingresar Montos
70 Error de formato Campo de Boleta MAX 6
71 Error de Largo Campo de Impresión
72 Error de Monto Venta, Debe ser Mayor que “0”
73 Terminal ID no configurado
74 Debe Ejecutar CIERRE
75 Comercio no tiene Tarjetas Configuradas
76 Supero Número Máximo de Ventas, Debe Ejecutar CIERRE
77 Debe Ejecutar Cierre
88 Error Cantidad Cuotas
93 Declinada
94 Error al Procesar Respuesta
96 Producto no disponible

Glosario de términos

Término
Definición
Venta Es una operación solicitada por el Comercio que genera una compra efectuada por un Tarjetahabiente con su Tarjeta de Crédito y Débito.
Anulación Es una operación solicitada por el Comercio que revierte una compra efectuada por un Tarjetahabiente con su Tarjeta de Crédito.
Cierre de Terminal Corresponde al cierre de las ventas diarias efectuadas en el terminal. Esta operación el comercio realiza diariamente y se consulta si desea imprimir el comprobante con el detalle de las ventas. Con el cierre se descargan desde el base24 los productos que tiene contratado el comercio, por ejemplo: Cuotas. Al momento de ejecutar un cierre se borran todas las transacciones almacenadas en la memoria del POS.
Carga de llaves Se realiza para sincronizar las llaves entre el terminal y el base24 de transbank. Esto se realiza cada vez que exista una actualización de la apk de pago y sus parámetros.
Detalle de ventas Corresponde a todas las transacciones que se han realizado y permanecen en la memoria del POS. (Un cierre de terminal, vacía la memoria).
Reimpresión de Voucher Reimpresión de ultima venta realizada
Base 24 Sistema Switch de las transacciones a los Emisores, administración de los Puntos de Ventas y Autorizador en contingencia de Transbank
Código Autorización Código de autorización de la transacción
Monto Monto total de venta
Moneda Corresponde a moneda PESO (Chilenos)
Monto Cuota Corresponde al monto cuota de la transacción. Por ejemplo, una venta de $24.000 en 3 cuotas corresponde a un monto cuota de $8000.
Tipo de Tarjeta Corresponde al tipo de tarjeta: CR=Crédito DB=Debito
Fecha Transacción Fecha de la transacción
Hora Transacción Hora de la transacción
Terminal ID Identificador del terminal
Código de comercio Identificador del comercio
Últimos 4 Dígitos Tarjeta Corresponde a los 4 últimos dígitos de la tarjeta de crédito o debito
Número Operación Correlativo de Transacción del Terminal
Empleado Valor numérico asociado al empleado del comercio. Este es solicitado por pantalla cuando el POS tiene habilitada esta opción y se imprime en el voucher o comprobante que genera la venta.
Propina Monto que corresponde a una recompensa económica a convenir con el cliente y que se otorga como agradecimiento por un buen servicio y por el producto consumido. El cliente decide si da o no una propina. La propina es parte del monto total de la transacción de venta.
Vuelto Vuelto corresponde ofrecer dinero efectivo cuando los clientes tarjetabientes pagan por venta Debito. El monto de la transacción es el valor de la venta más el vuelto solicitado. Por ahora se ofrece: $1.000, $5.000, $10.000 y $20.000. Si cliente no quiere vuelto valor debe ir en 0.
Número de boleta El número de boleta corresponde al valor numérico asociado al comprobante de pago. Este es solicitado por pantalla cuando el POS tiene habilitada esta opción y se imprime en el voucher o comprobante que genera la venta.
Tipo de Documento Tipo de Documento corresponde (39: Boleta Electrónica Afecta 41: Boleta Electrónica Exenta). La boleta electrónica es un documento tributario, generado y firmado electrónicamente, da cuenta de las ventas de bienes o de la prestación de algún servicio a un consumidor final. Cuando la boleta es exenta significa que no se encuentra afecta al impuesto al valor agregado (IVA)
Código Respuesta Corresponde a los códigos de respuesta que puede devolver la transacción. Ver significado en ítem códigos de respuesta.
Cantidad Crédito Corresponde a la cantidad total de transacciones realizadas con tarjeta de crédito
Cantidad Débito Corresponde a la cantidad total de transacciones realizadas con tarjeta de débito
Cantidad Otros Medios Corresponde a la cantidad total de transacciones realizadas con otros medios, por ejemplo: efectivo
Monto Crédito Corresponde al monto total de ventas realizadas con tarjeta de crédito
Monto Débito Corresponde al monto total de ventas realizadas con tarjeta de débito
Monto Otros Medios Corresponde al monto total de ventas realizadas con otros medios, por ejemplo: efectivo
Monto total Corresponde a la suma total de montos: crédito, débito y otros medios

Glosario de términos: Abreviación de Tarjeta

Corresponde a la sigla de abreviación de tarjetas:

Tarjeta
Abreviación
VISA VI
MASTERCARD MC
CABAL CA
CREDENCIAL CR
AMEX AX
CERRADA CE
DINNERS DC
PRESTO TP
MAGNA MG
MAS (CENCOSUD) TM
RIPLEY RP
EXTRA EX
CMR EX
REDCOMPRA DB

FontSize

enum class FontSize { NORMAL {
override fun getIntValue() = 0 },
BIG {
override fun getIntValue() = 1
}, BIGGEST {
override fun getIntValue() = 2 };
abstract fun getIntValue(): Int
companion object {
fun getFontStyleForInt(intValue: Int) = when (intValue) {
1 -> BIG
2 -> BIGGEST else -> NORMAL
} 
}
}

TextStyle

enum class TextStyle { NORMAL {
override fun getIntValue() = 0 },
BOLD {
override fun getIntValue() = 1
}, UNDERLINE {
override fun getIntValue() = 2 };
abstract fun getIntValue(): Int
companion object {
fun getTextStyleForInt(intValue: Int) = when (intValue) {
0 -> NORMAL
1 -> BOLD
else -> UNDERLINE
} }
}

FontType

enum class FontType { COUSINE {
override fun getIntValue() = 1 },
YAHEIB {
override fun getIntValue() = 2
};
abstract fun getIntValue(): Int
companion object {
fun getFontTypeForInt(intValue: Int) = when (intValue) {
1 -> COUSINE
else -> YAHEIB }
} }

PrinterResponseCodes

Clase que corresponde a los códigos de respuesta de la impresora.

class PrinterResponseCodes { companion object {
const val SUCCESS = 0
const val PRINTER_IS_BUSY = 1 const val OUT_OF_PAPER = 2
const val FORMAT_ERROR = 3
const val PRINTER_ERROR = 4
const val PRINTER_OVER_HEAT = 8
const val PRINTER_VOLTAGE_TOO_LOW = 9 const val PRINTER_UNFINISHED = -16
const val NOT_FONT_LIBRARY = -4
const val DATA_PACKAGE_IS_TOO_LOMG = -2
} }

ErrorResponseCodes

Esta clase corresponde a los códigos de respuesta que son utilizados cuando se recibe la respuesta de una operación.

class rorResponseCodes { companion object {
const val COULD_NOT_LOAD_KEYS = -1
const val LAST_VOUCHER_DOES_NOT_EXIST = -2 const val NOT_AUTHORIZED = -3
const val COULD_NOT_START_PRINTER = -4
const val COULD_NOT_FINISH_PRINTING = -5 const val APP_NOT_EXIST = -6
const val APPROVED=0;
const val REJECTED=1;
const val ERROR_CONNECTION_FAIL=3;
const val ERROR_TRANSACTION_WAS_CONSULTED_BEFORE=4;
const val ERROR_TRANSACTION_NOT_EXIST_ANNULMENT=5;
const val ERROR_CARD_NOT_SUPPORTED=6;
const val ERROR_TRANSACTION_CANCEL_BY_POS=7;
const val ERROR_TRANSACTION_CANT_ANNULMENT_DEBIT=8;
const val ERROR_READ_CARD=9;
const val ERROR_MINIMUM_AMOUNT=10;
const val ERROR_SALE_NOT_EXIST=11;
const val ERROR_TRANSACTION_NOT_SUPPORTED=12;
const val ERROR_MUST_EXECUTE_CLOSE_13=13;
const val ERROR_NOT_VALID_MENU=18;
const val ERROR_DIFFERENT_CARD=19;
const val ERROR_CARD_NOT_VALID=20;
const val ERROR_ANNULMENT_NOT_ALLOW=21;
const val ERROR_TIME_OUT=22;
const val ERROR_PRINTER_WITHOUT_PAPER=24;
const val ERROR_INVALID_DATE=25;
const val ERROR_MUST_LOAD_KEYS=26;
const val ERROR_MUST_UPDATE=27;
const val ERROR_MUST_SEND_TRADE_LENDER=28;
const val ERROR_NUMBER_DUES=60;
const val ERROR_REQUEST_ASSEMBLY=61;
const val ERROR_PROCESS_HOST_RESPONSE=65;
const val ERROR_EXCEED_MAXIMUM_OF_SALES=67;
const val ERROR_GENERIC_AMMOUNT=68;
const val ERROR_FORMAT_BALLOT_FIELD=70;
const val ERROR_LONG_FIELD_PRINT=71;
const val EEROR_AMMOUNT_SALE_MUST_GREATER_THAN_ZERO=72; const val ERROR_TERMINAL_ID_NOT_CONFIGURED=73;
const val ERROR_MUST_EXECUTE_CLOSE_74=74;
const val ERROR_TRADE_NOT_CONFIGURED_CARD=75;
const val ERROR_EXCEED_MAXIMUM_SALES_MUST_EXECUTE_CLOSE=76; const val ERROR_MUST_EXECUTE_CLOSE_77=77;
const val ERROR_AMOUNT_OF_DUES=88;
const val ERROR_DECLINED=93;
const val ERROR_PROCESS_RESPONSE=94;
const val ERROR_PRODUCT_NOT_AVAILABLE=96;
} }

TotalVentasResponse

Esta clase es utilizada para mostrar la información que aparece en el reporte de totales en la aplicación de pagos.

En donde montoCredito corresponde al monto total por ventas de crédito, montoDebito que corresponde al monto total de ventas por débito.

El atributo montoOtro corresponde al monto total de ventas por otros medios y montoTotal corresponde a la sumatoria de montoCredito, montoDebito y montoOtro.

El atributo cantCredito corresponde a la cantidad total de ventas por crédito, cantDebit corresponde a la cantidad total de ventas débito, cantOtro corresponde a la cantidad total de ventas por otros medios.

Finalmente cantTotal corresponde a la sumatoria de cantCredito, cantDebito y cantOtro.

class TotalVentasResponse( 
val montoCredito: Int?,
val montoDebito: Int?, 
val montoOtro: Int?
val montoTotal: Int?, 
code cantCredito: Int?, 
code cantDebito: Int?, 
code cantOtro: Int?, 
code cantTotal: Int?
)
	

ReprintVoucherResponse

Esta clase es utilizada para mostrar la información cuando se ejecuta imprimir último voucher. Contiene los atributos relacionados a una venta.

Ya que al imprimir el último voucher de venta, lo que hace es buscar la última venta registrada y con esta clase se define la informacion a mostrar sobre la ultima venta.

class ReprintVoucherResponse(
val codigoAutorizaci��n: String?, val monto: String?,
val moneda: String?,
val numeroCuotas: String?,
val montoCuota: String?,
val medioPago: String?,
val fechaTransacci��n: String?,
val horaTransacci��n: String?,
val terminalID: String?,
val codigoComercio: String?,
val ultimos4DigitosTarjeta: String?, val numeroOperacion: String?,
val numeroCuenta: String?,
val abreviacionTarjeta: String?, val empleado: String?,
val propina: String?,
val vuelto: String?,
val numeroBoleta: String?,
val tipoDocumento: String?, 
val codigoRespuesta: String?, 
val glosaRespuesta: String?
)

ProcesoResponse

Clase genérica que sirve para mostrar los resultados de una operación. Por ejemplo al realizar un cierre y este se realizó de manera exitosa, entonces se utiliza esta clase para mostrar el resultado del cierre por ejemplo.

En donde codigoRespuesta corresponde al código de respuesta de la operación, codigoComercio corresponde a la descripción del código de respuesta. Finalmente terminalId corresponde a la DDLL del terminal

class ProcesoResponse(
code codigoRespuesta: String?, 
code codigoComercio: String?, 
code terminalId: String?
)

AnulacionResponse

Esta clase es utilizada para mostrar la información cuando se realiza una anulación de venta. En donde codigoRespuesta indicará el código de respuesta de la anulación, por ejemplo si la tarjeta no corresponde a una de crédito, en codigoRespuesta tendrá el valor de código de respuesta cuando la tarjeta no corresponde.

En glosaRespuesta contiene la descripción del código de respuesta. En caso de que la anulación se ejecute de manera exitosa. Entonces en codigoRespuesta tendrá el valor 0 y glosaRespuesta tendrá el valor de Aprobado.

El resto de atributos corresponde a la información relacionada sobre la anulación de la venta.

class AnulacionResponse( 
al codigoComercio: String?, 
val terminalID: String?,
val codigoAutorizacion: String?, 
val numeroOperacion: String?, 
val codigoRespuesta: String?, 
val glosaRespuesta: String?
)

VentaResponse

Esta clase es utilizada para mostrar la información cuando se realiza una venta. Si la venta se realiza de manera exitosa entonces codigoRespuesta será 0 que significa operación ejecutada con éxito.

El atributo glosaRespuesta en caso de que la venta se realiza de manera exitosa, tendrá el valor de Aprobado. En caso de venta errónea entonces codigoRespuesta tendrá el código de respuesta de error correspondiente y glosaRespuesta tendrá la descripción del código de

respuesta de error en esta caso.

El resto de atributos corresponden a la información sobre la venta.

class VentaResponse(
val codigoAutorizaci��n: String?, val monto: String?,
val moneda: String?,
val numeroCuotas: String?,
val montoCuota: String?,
val medioPago: String?,
val fechaTransacci��n: String?,
val horaTransacci��n: String?,
val terminalID: String?,
val codigoComercio: String?,
val ultimos4DigitosTarjeta: String?, val numeroOperacion: String?,
val numeroCuenta: String?,
val abreviacionTarjeta: String?,
val empleado: String?,
val propina: String?,
val vuelto: String?,
val numeroBoleta: String?,
val tipoDocumento: String?,
val codigoRespuesta: String?,
val glosaRespuesta: String?
val idUnico: String?,
val montoBoleta: String?,
val ivaBoleta: String?,
val ted: String?,
)

BundleKeys

Contiene los identificadores únicos de los distintos BundleKeys que son utilizados en las distintas funciones de aplicación de pagos.

class BundleKeys(
const val ANNULMENT_KEY = "APP_VA_ANULACION"
const val ANNULMENT_NUMBER_KEY = "APP_VA_NUM_ANULACION"
const val SALES_KEY = "APP_VA_VENTA"
const val API_KEY_KEY = "apiKey"
const val ERROR_KEY = "ERROR"
const val TOTALES_KEY = "APP_VA_REPORTE_TOTALES"
const val REPRINT_KEY = "APP_VA_TRX_REIMPRIMIR_ULT"
const val LOAD_KEYS_KEY = "APP_VA_CARGA_LLAVES"
const val CLOSE_TERMINAL_KEY = "APP_VA_CIERRE_TERMINAL"
const val SALES_AMOUNT_KEY = "APP_VA_HOME_MONTO"
const val SALES_CURRENCY_TYPE_KEY = "APP_VA_HOME_TIPO_MONEDA" const val LINES_FOR_PRINTER_KEY = "LINES_FOR_PRINTER"
const val PRINT_KEY = "PRINT"
)

Explicación de parámetros de entrada de las funciones

Para ejecutar las funciones como cierre, carga llaves,ventas, etc. Es necesario saber que parametros de entrada debo utilizar para ejecutar las funciones.

Aqui un ejemplo de la ejecución del metodo carga llaves en donde se pueden ver los parametros de entrada this, apiKey y flavor que son los parámetros de entrada que se utilizan en todas las funciones.

RequestStatus status = sdkActivityLauncher.requestForLoadKeys(this, apiKey, flavor); 

En donde this corresponde al AppCompatActivity, apiKey corresponde al API KEY de su aplicación que está implementando librería.

Finalmente en donde dice flavor. Corresponde al flavor de la aplicación de valor agregado.

El parámetro de entrada flavor es utilizado porque la librería al momento de ejecutar el intent hacia la aplicación de pagos. Este necesita saber el nombre de package completo de la aplicación de pago para ejecutar correctamente el intent que hace la librería hacia la aplicación de pagos.

Por lo que si se está trabajando con la aplicación de pagos versión desarrollo, esta se debió compilar con el flavor desarrollo que es "dev" por ejemplo.

Entonces desde la aplicación de valor agregado para implementar la librería puede utilizar el mismo flavor de compilación que la aplicación de pagos (flavor "dev") para así enviarle este parámetro.

SDK Control Entre Interfaces (CEI)

CEI - SDK de periféricos es una aplicación para dar acceso a los periféricos (Impresora y lector de QR,) generando una comunicación encapsulada y segura entre aplicaciones externas y el hardware de los dispositivos A920, A920 Pro e IM30.

El SDK CEI entrega una forma simple de hacer uso e interactuar con los diferentes periféricos que tengan los dispositivos de tecnología Android. Este SDK implementa métodos que ayudan a hacer los llamados a los diferentes periféricos como impresora, escáner QR de manera simple, directa y segura. Más adelante se detalla la forma de utilizar este.

Requerimientos mínimos

  • Sdk mínimo api level 24(Nougat)

  • Java 1.8
  • Kotlin 1.4.2

  • Dispositivo Android

Consideraciones

  • Todas las aplicaciones que utilicen este SDK deben pasar por un proceso de certificación que garantice su buen uso.

  • Tener instalada la App CEI.

  • Descarga la librería sia-peripheral-control-sdk-1.2.0.aar o en su última versión

Instalación e implementación de librería (aar)

  1. Agrega el archivo aar:
  2. En el archivo build.gradlede aplicación agrega lo siguiente:
    implementation files('libs/sia-peripheral-control-sdk-1.2.0.aar')
  3. Sincroniza el proyecto:
    • Clic en File > Sync Project with Gradle Files

Características principales del SDK

  • Solicitud de Scanner QR -> Activa la cámara o escáner para poder leer códigos QR y obtener la información que este contiene.
  • Solicitud de impresión -> Permite generar una impresión, siempre y cuando el dispositivo lo soporte, y el formato de la cadena a imprimir cumpla con las condiciones.

Uso

Inicialización del SDK

Para utilizar el SDK se debe crear una instancia de tipo SdkActivityLauncher, la cual se inicializa de la siguiente manera (this hace referencia al contexto de la actividad donde se instancia):


private val sdkLauncher = SdkActivityLauncher(this)

Crear contrato de registro de resultado de la Actividad

Crear el siguiente contrato en la actividad donde se crea la instancia de tipo SdkActivityLauncher:


private val startForResult = registerForActivityResult(
	ActivityResultContracts.StartActivityForResult()
) { result: ActivityResult ->
	val key = result.data?.extras?.keySet()?.first()
	when(key) {
		BundleKeys.QR_SCANNER_KEY -> {
			// Implement response processing code
		}
		BundleKeys.PRINTER_KEY -> {
			// Implement response processing code
		}
		BundleKeys.ERROR -> {
			// Implement response processing code
		}
		else -> Log.d(TAG, "BundleKeys no exist $key")
	}
}

Scanner QR

Invocar el Scanner QR

Se invoca la función requestForScannerQR() la cual recibe como parámetro el contrato registrado previamente


sdkLauncher.requestForScannerQR(startForResult)

Procesar respuesta Scanner QR

Se da como ejemplo la función processResponseScannerQr() para procesar la respuesta de la solicitud de Scanner QR, la cual recibe como parámetros el código de respuesta de la solicitud y el Intent de la actividad, esta función se debe llamar en el contrato cuando el caso sea igual BundleKeys.QR_SCANNER_KEY.


when(key) {
	...
	BundleKeys.QR_SCANNER_KEY -> {
		processResponseScannerQr(result.resultCode, result.data)
	}
	...
}



private fun processResponseScannerQr(resultCode: Int, intentIn: Intent?) {
	val response: BundleWrapper = sdkLauncher.getScannerQrResponseResult(
		resultCode = resultCode,
		data = intentIn
	)
	when(response.status) {
		RequestStatus.REQUEST_OK -> {
			val scannerResponse: QrScannerResponse = response.response as QrScannerResponse
			scannerResponse.content?.let {
				// Implement your logic, QR with Data
			}
		}
		RequestStatus.REQUEST_CANCELLED_WITHOUT_ERROR,
		RequestStatus.REQUEST_CANCELLED_WITH_ERROR-> {
			val errorResponse = response.response as ErrorResponse
			// Implement your logic, QR with error or cancelled
		}
		else -> {
			// Implement your logic, no information
		}
	}
}

Impresión

Para poder procesar una impresión se debe enviar una cadena formateada y cumplir con las condiciones establecidas que más adelante se relacionan en el apartado de Generar voucher

Generar voucher

Para la construcción del voucher se debe tener en cuenta las siguientes premisas:

  • Cada fila está conformada por 32 caracteres
  • Cada voucher solo podrá contener máximo 50 filas
  • El contenido de cada fila no necesariamente debe contener los 32 caracteres, pero si por ejemplo una fila tiene 10 caracteres, estos por defecto quedarán centrados en la fila
  • Para la construcción del voucher hacer uso de la clase PrintUtils
  • Siempre que se termine de usar PrintUtils para la construcción del voucher, se debe invocar la función resetVoucher() (PrintUtils.resetVoucher()) que permite limpiar el voucher en memoria

Crear fila de voucher
Se hace uso de la clase PrintUtils y se invoca la función setLineVoucher() que recibe como parámetro el contenido de la fila


PrintUtils.setLineVoucher("row content")

Obtener voucher formateado
Se hace uso de la clase PrintUtils y se invoca la función getFormattedVoucher()


PrintUtils.getFormattedVoucher()

Ejemplo

Voucher


// Construimos el cuerpo del voucher
val repeatC = "C".repeat(20)
val repeatL = "L".repeat(20)
val repeatR = "R".repeat(20)
PrintUtils.setLineVoucher("INICIO") 
PrintUtils.setLineVoucher(repeatC) 
PrintUtils.setLineVoucher("$repeatL 			")
PrintUtils.setLineVoucher("$repeatL 			")
PrintUtils.setLineVoucher("$repeatL 			")
PrintUtils.setLineVoucher("				$repeatR")
PrintUtils.setLineVoucher("				$repeatR") 
PrintUtils.setLineVoucher(repeatC) 
PrintUtils.setLineVoucher(repeatC) 
PrintUtils.setLineVoucher(repeatC) 
PrintUtils.setLineVoucher(Date().toString().take(20))
PrintUtils.setLineVoucher(" ") 
PrintUtils.setLineVoucher("GRACIAS POR SU COMPRA") 
PrintUtils.setLineVoucher("FIN")
// Obtenemos el contenido del voucher 
PrintUtils.getFormattedVoucher()
// Reseteamos contenido del voucher para poder crear uno nuevo 
PrintUtils.resetVoucher()

Resultado

Como resultado, obtenemos una sola cadena formateada:

//             INICIO                   CCCCCCCCCCCCCCCCCCCC 
LLLLLLLLLLLLLLLLLLLL            LLLLLLLLLLLLLLLLLLLL 
LLLLLLLLLLLLLLLLLLLL                        RRRRRRRRRRRRRRRRRRRR
RRRRRRRRRRRRRRRRRRRR      CCCCCCCCCCCCCCCCCCCC            CCCCCCCCCCCCCCCCCCCC
CCCCCCCCCCCCCCCCCCCC            Tue Nov 21 21:00:04
GRACIAS POR SU COMPRA                    FIN

Que en el voucher impreso se vería algo como:

/*
             INICIO
      CCCCCCCCCCCCCCCCCCCC
LLLLLLLLLLLLLLLLLLLL
LLLLLLLLLLLLLLLLLLLL
LLLLLLLLLLLLLLLLLLLL
            RRRRRRRRRRRRRRRRRRRR 
            RRRRRRRRRRRRRRRRRRRR
      CCCCCCCCCCCCCCCCCCCC
      CCCCCCCCCCCCCCCCCCCC
      CCCCCCCCCCCCCCCCCCCC
      Tue Nov 21 21:00:04

     GRACIAS POR SU COMPRA
              FIN
*/

Invocar la impresión

Se invoca la función requestForPrinter() la cual recibe como parámetros el contrato registrado previamente y objeto de tipo Printer el cual necesita de una cadena formateada que será la que se imprimirá (hacer uso de la clase PrintUtils para su construcción).


sdkLauncher.requestForScannerQR(startForResult)

Procesar respuesta Impresora

Se da como ejemplo la función processResponsePrinter() para procesar la respuesta de la solicitud de impresión, la cual recibe como parámetros el código de respuesta de la solicitud y el Intent de la actividad, esta función se debe llamar en el contrato cuando el caso sea igual BundleKeys.PRINTER_KEY.


when(key) {
	...
	BundleKeys.PRINTER_KEY -> {
		processResponsePrinter(result.resultCode, result.data)
	}
	...
}


private fun processResponsePrinter(resultCode: Int, intentIn: Intent?) {
	val response: BundleWrapper = sdkLauncher.getPrinterResponseResult(
		resultCode = resultCode,
		data = intentIn
	)
	when(response.status) {
		RequestStatus.REQUEST_OK -> {
			val printerResponse: PrinterResponse = response.response as PrinterResponse
			if(printerResponse.status == PrinterResponseCodes.SUCCESS) {
				// Implement your logic, successful impression
			} else {
				// Implement your logic, print NOT executed
			}
		}
		RequestStatus.REQUEST_CANCELLED_WITHOUT_ERROR -> {
			val errorResponse = response.response as ErrorResponse
			// Implement your logic, print with error
		}
		else -> {
			// Implement your logic, no information
		}
	}
}

Procesamiento de errores

Se da como ejemplo la función processResponseError() para procesar la respuesta con error, la cual recibe como parámetros el código de respuesta de la solicitud y el Intent de la actividad, esta función se debe llamar en el contrato cuando el caso sea igual BundleKeys.ERROR.


when(key) {
	...
	BundleKeys.ERROR -> {
		processResponseError(result.resultCode, result.data)
	}
	...
}



private fun processResponseError(resultCode: Int, intentIn: Intent?) {
	val response = sdkLauncher.getErrorResponseResult(resultCode, intentIn)
	val errorResponse = response.response as ErrorResponse 
	errorResponse.glossResponse?.let {
		// Implement your logic, no information
	}
}

Invocar funciones del SDK desde PWA

Para poder invocar funciones del SDK desde una PPWA se debe implementar la interfaz HwWebInterface el wrappper nativo android y luego agregarlo al WebView que despliega la PWA, para luego acceder a cada una de las funciones del SDK como se indica en cada una de sus funciones.


	// implementación de interfaz
	val hwWebInterface = HwWebInterface(object : HwJavascriptInterface {
		override fun requestForScannerQR() {
			sdkLauncher.requestForScannerQR(startForResult)
		}
	 
	   override fun requestForScannerHw() {
		   sdkLauncher.requestForScannerHw(startForResult)
	   }
	 
	   override fun (lines: String) {
		   sdkLauncher.requestForPrinter(
					  startForResult, Printer(lines)
		   )
	   }
	})
	 
	// Set interfaz en WebView de PWA
	webView.addJavascriptInterface(hwWebInterface, HwWebInterface.HARDWARE_SDK_INTERFACE_NAME)
	

Para la impresión se recomienda pasar un arreglo de String desde la PWA a y luego procesarlo y darla formato línea por línea. Ejemplo de texto a imprimir desde PWA


	{"lines":["INICIO","linea 2","linea 3","FIN"]}

Para invocar los métodos del sdk desde la aplicación web se debe hacer desde el script JavaScript de la PWA.


	function scanQR(){
		HARDWARESDK.requestForScannerQR();
	}
	 
	function scanBarCode(){
		HARDWARESDK.requestForScannerHw();
	}
	function callPrintHw(){
		HARDWARESDK.requestForPrinter( JSON.stringify(
			{"lines":["INICIO","linea 2","linea 3","FIN"]}
		));
	}

Definiciones y descripción de clases

/**
	* Contiene los identificadores únicos de los distintos BundleKeys que son 
	utilizados en las distintas funciones de aplicación.
*/
object BundleKeys {
	const val QR_SCANNER_KEY = "QR_SCANNER"
	const val PRINTER_KEY = "PRINTER"
	const val ERROR = "ERROR"
}
/**
	* Clase donde se almacena la respuesta de la solicitud Scanner QR o [Printer]
	* @param status de tipo [RequestStatus]
	* @param response que puede ser [PrinterResponse] o [QrScannerResponse] o
	[ErrorResponse]
*/
class BundleWrapper (
	val status: RequestStatus,
	val response: Parcelable?
)
/**
	* Contiene los diferentes estados que puede presentar una solicitud
*/
enum class RequestStatus {
	REQUESTED,
	REQUEST_OK, 
	REQUEST_ERROR,
	REQUEST_CANCELLED_WITHOUT_ERROR, 
	NOT_REQUESTED, 
	REQUEST_CANCELLED_WITH_ERROR
}
/**
	* Contiene los diferentes códigos de error que puede presentar una solicitud
*/
object  ErrorResponseCodes  {
	const val ERROR_UNKNOWN = -100
	const val ERROR_OBJ_PARCEABLE = -101
	const val ERROR_BUNDLE_KEY = -102 
	const val TECH_NOT_SUPPORTED = -103 
	const val NOT_FOUND_PRINTER = -104 
	const val APP_NOT_EXIST = -404
	const val REQUEST_CANCELED = -500
	const val TIMEOUT = -504
}
/**
	* Contiene los diferentes códigos de respuesta a una solicitud de impresión
*/
object PrinterResponseCodes {
	const val SUCCESS = 0
	const val PRINTER_IS_BUSY = 1 
	const val OUT_OF_PAPER = 2 
	const val FORMAT_ERROR = 3 
	const val PRINTER_ERROR = 4 
	const val PRINTER_OVER_HEAT = 8
	const val PRINTER_VOLTAGE_TOO_LOW = 9
	const val PRINTER_UNFINISHED = -16 
	const val NOT_FONT_LIBRARY = -4
	const val DATA_PACKAGE_IS_TOO_LOMG = -2
}