Implementando el SDK en tu proyecto

En esta sección aprenderás a usar nuestro SDK para controlar las operaciones del lector NetPay MINI

1. Implementando las interfaces

Crea una actividad (activity) que implemente las siguientes interfaces: ITransactionListener, IReportsListener y sus métodos correspondientes.

Implementación

class KotlinActivity : AppCompatActivity(), ITransactionListener, IReportsListener, IPasswordListener {
    override fun selectedAppAction(apps: List<String>, action: (Int) -> Unit) {
        TODO("Not yet implemented")
    }

    override fun valuesToProcessing(values: 
                                    (total: Double, 
                                    tip: Double, 
                                    reference: String, 
                                    promotion: String) -> Unit) {
        TODO("Not yet implemented")
    }

    override fun transactionsResult(message: String, processed: Boolean) {
        TODO("Not yet implemented")
    }

    override fun posTransactionEnumResult(result: NpTransactionEnum) {
        TODO("Not yet implemented")
    }

    override fun reverseResult(message: String, processed: Boolean) {
        TODO("Not yet implemented")
    }

    override fun refundResult(message: String, processed: Boolean) {
        TODO("Not yet implemented")
    }

    override fun errorResult(npErrorEnum: NpErrorEnum) {
        TODO("Not yet implemented")
    }

    override fun provideSignaturePathResult(intent: Intent) {
        TODO("Not yet implemented")
    }

    override fun startingPaymentProcessing() {
        TODO("Not yet implemented")
    }

    override fun imageResult(processed: Boolean, image: String?, message: String?) {
        TODO("Not yet implemented")
    }

    override fun changePassword(
        isSuccess: Boolean,
        messageError: String?,
        result: RecoverPasswordResponse?
    ) {
        TODO("Not yet implemented")
    }

    override fun reportSalesByDateAndUserResult(
        processed: Boolean,
        message: String?,
        reportSales: List<ResponseSale>?
    ) {
        TODO("Not yet implemented")
    }

    override fun reportSaleDetailsResult(response: ResponseSaleDetail?, success: Boolean) {
        TODO("Not yet implemented")
    }
}

2. Declarando e inicializando variables

Antes de implementar los métodos anteriores debemos de clarar e inicializar los objetos que contienen las funcionalidades que vamos a utilizar.

Declaración global de variables

private val connectReader: IConnectReader by lazy { ConnectReaderDevice(this) }
private lateinit var transaction: INpTransactions
private lateinit var reports: INpReports
private lateinit var miniPreferences: IMiniPreferences
private lateinit var binding: ActivityMainBinding
private lateinit var passwordUpdate: INpPassword
private lateinit var bluetoothManager: BluetoothManager

Inicializar los objetos en el método onCreate()

Implemenación

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    // Initialize actions for transactions
    transaction = NpTransactions(this, connectReader, this)
    reports = NpReports(this, this)
    miniPreferences = MiniPreferences(this)
    passwordUpdate = NpPassword(this,this)
    bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager

    //TODO IMPORTANTE #1 setInitializeSDK
    val userNameDefault = "[email protected]"
    val userPass = "Y0uRP4s5w0rD"

    miniPreferences.setInitializeSDK(userNameDefault, userPass, ConfigSdk.DEV)
}

📘

Información

El objeto ConfigSdk, tiene las constantes necesarias para trabajar sobre ambiente Sandbox.

  • Sandbox: ConfigSdk.DEV
FieldTypeDescription
userNameDefaultStringNetPay user.
passwordStringPassword.
ConfigSdkStringEnvironment.

3. Conectando el dispositivo

Inicializa el objeto connectReader en el método onCreate().

Implementación

connectReader.initialize { result ->
  when (result) {
    is ConnectDiscoveryState.Started -> {
      Toast.makeText(this, "find device started", Toast.LENGTH_SHORT).show()
    }
    is ConnectDiscoveryState.Found -> {
      val device = result.deviceConnected
      connectAndCancelFindingDevice(
         result.deviceConnected.deviceName, 
         result.deviceConnected.deviceAddress
      )

      binding.startTransactionAction.apply {
        text = "Iniciar Transacción: ${device.deviceName}"
      }
      enableButton(binding.startTransactionAction)
      Toast.makeText(this, "Device found: ${device.deviceName}", Toast.LENGTH_SHORT).show()
    }
    is ConnectDiscoveryState.Finished -> {
      Toast.makeText(this, "finish finding device", Toast.LENGTH_SHORT).show()
    }
  }
}

Registra el dispositivo en el método onStart().

Implementación:

override fun onStart() {
  super.onStart()
  connectReader.registerReceiver()
}

Agrega la función startConnectReaderDevice() e invocala en el método onRequestPermissionResult() una vez que el permiso haya sido concedido.

Implementación:

private fun startConnectReaderDevice() {
    connectReader.findAdapterDevices { result ->
      if (!connectReader.adapterIsEnabled()) {
        startActivityForResult(
          connectReader.adapterEnableRequestIntent(),
          ENABLE_BT_REQUEST_CODE
        )
      }
      Log.i("FIND ADAPTER MSG", result.message)
    }
  }

Crea una función para mostrar los resultados de la conexión y llámala durante la inicialización del lector connectReader

Implementation:

private fun connectAndCancelFindingDevice(deviceName: String, deviceAddress: String) {
	connectReader.registerDevice(deviceName, deviceAddress)
}

Ejecuta la función connectAndCancelFindingDevice() después de seleccionar el dispositivo.

FieldTypeDescription
deviceNameStringNombre de tu dispositivo MINI
deviceAddressStringDirección MAC del lector NetPay MINI

4. Generando una transacción

Inicializa una transacción con los siguientes datos:

transaction.initialize(
  config = ConfigSdk.DEV,
  userName = userNameDefault,
  password = //contraseña,
  requireSignature = false
)
CampoTipoDescripción
configStringIndica el ambiente sobre el cual se estará trabajando.
userNameStringNombre de usuario.
passwordStringContraseña.
requireSignatureBoolIndica si el estará habilitada la funcionalidad de solicitar firma digital durante el proceso de cobro, si esta bandera es false significa que el voucher no tendrá la firma digital impresa.

Implementar el método callback valuesToProcessing(...).

override fun valuesToProcessing(values: (total: Double, tip: Double, reference: String, promotion: String) -> Unit) {
    val total = binding.editAmount.text.toString()

    val referenceText = binding.editTextReference.text.toString()
    val referenceEncoded = encodeString(referenceText)

    val promSelected = binding.spinnerPromotion.text.toString()
    val promotionSelected = PromotionEnum.values().find { it.text == promSelected }
    promotionSelected?.let { values(total.toDouble(), 0.0, referenceEncoded, promotionSelected.value) }
  }

CampoTipoDescripción
referenceStringReferencia de la transacción. Este valor es generado por el comercio.
totalDoubleMonto total a cobrar (incluyendo propina).
promotionStringIndica si la transacción se cobrará a meses sin intereses (3, 6, 9, 12 ó 18). Esto se implementó en el código anterior haciendo uso de una clase Enum.
tipDoubleMonto de propina.

Algunas tarjetas pueden tener múltiples aplicaciones, para estos casos, usen el método selectedAppAction().

5. Obteniendo la respuesta de una transacción

El resultado de una transacción será recibido por el método callback **transactionsResult()**.

override fun transactionsResult(message: String, processed: Boolean) {
    runOnUiThread {
      Toast.makeText(this, message, Toast.LENGTH_SHORT).show()

      if (processed) {
        Log.d(TAG, "transactionsResult message: $message")
        refundLastTransactionId(message.split(":")[1])
        binding.editOrderId.setText(message.split(":")[2])
        binding.tvResponse.setText(message)
        binding.refundAction.setText("Refund: ${message.split(":")[2]}")
      }
    }
  }
CampoTipoDescripción
messageStringEn caso de una transacción aprobada, aquí se estarán recibiendo el transactionTokenId y el orderId.
processedBoolIndica si la transacción fue exitosa.

En caso de que no sea recibida la respuesta de una transacción habrá un reverso, esto indica que el resultado de la transacción será mostrado por el método callback reverseResult(). Cuando una transacción se reversa, esta se cancela de forma automática aún cuando haya sido aprobada.

override fun reverseResult(message: String, processed: Boolean) {
    runOnUiThread {
      Toast.makeText(this, "reverse: $message", Toast.LENGTH_SHORT).show()
    }
  }

6. Cancelando una transacción

Para poder cancelar una transacción es necesario contar con el transactionId de la transacción.

transaction.processRefund(
  transactionId = transactionId
)
FieldTypeDescription
transactionIdStringIdentificador de la transacción generado por NetPay y recibido en la respuesta

7. Obteniendo la respuesta de una transacción cancelada

La respuesta de una transacción cancelada se recibirá en el método refundResult().

override fun refundResult(message: String, processed: Boolean) {
    runOnUiThread {
      Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
      binding.refundAction.text = "refund: $message"
    }
  }

8. Visualizando un reporte de ventas

Para consultar un reporte de ventas se debe proporcionar un rango de fechas.

reports.getReportSalesByDateAndUser(
        startDate = sdf.format(Date()),
        endDate = null,
        userId = null
      ) {
        runOnUiThread {
          Log.w(TAG, it)
          Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
        }
      }
FieldTypeDescription
startDateStringFecha inicial desde donde se iniciará la búsqueda. Formato admitido: yyyy-MM-dd.
endDateString (Opcional)Fecha final desde donde se terminará la búsqueda. Formato admitido: yyyy-MM-dd. En caso de requerir solo un valor, puede dejar este parámetro con valor null.
userIdString (Opcional)Identificador del usuario que realizó la venta. Si es null este método devolvera todas las transacciones.

9. Obteniendo la respuesta de un reporte de ventas

La respuesta de este servicio regresa un arreglo con la toda la información de las ventas en el rango de fechas dado. Esta respuesta se recibirá en el método reportSalesByDateAndUserResult().

override fun reportSalesByDateAndUserResult(
    processed: Boolean,
    message: String?,
    reportSales: List<ResponseSale>?
  ) {
    Log.i(TAG, "reportSalesByDateAndUserResult")
    reportSales?.let {
      val resultStr: String = Gson().toJson(reportSales)
      binding.tvResponse.text = resultStr
      //Gson
      //val sales = fromJson(reportSales.report?.valueList, SalesResponseValue::class.java).value
    }
    message?.let {
      Log.i(TAG, "message: $message")
    }
  }

Ejemplo del objeto JSON recibido.

{
  "contentType": "Report",
  "resourceName": "ReportSalesByDateAndUser",
  "report": {
    "valueList": {
      "value": [
        {
          "orderId": "5227220201019144140737",
          "transactionId": "1511f0ba-284d-460f-9e33-e66507a96106",
          "cardNumber": "9898",
          "userId": "36142",
          "transTypeCd": "A",
          "transStatus": "C",
          "transDate": "2020-10-19T14:41:41-0500",
          "authCode": "222222",
          "amount": "2.00",
          "responseCode": "00",
          "responseMsg": "Aprobada",
          "authDate": "2020-10-19T14:41:41-0500",
          "userName": "[email protected]",
          "ticketID": "",
          "voucherID": "",
          "tipAmount": 0.0,
          "latitude": 25.654299,
          "longitude": -100.215834,
          "web": false
        }
      ]
    }
  },
  "response": {
    "approved": true,
    "responseCode": "00",
    "responseMsg": "Aprobada",
    "timeIn": "1603301717167",
    "timeOut": "1603301717628",
    "type": "JSON"
  }
}
CampoDescripciónTipoValor de ejemplo
orderIdIdentificador de la orden, generado por NetPayString"5227220201019134607260"
transactionIdIdentificador de la. transacción, generado por NetPayString"e4a7844f-95e6-4cc8-ad6d-93960b1f9e7e"
cardNumberÚltimos 4 dígitos de la tarjetaString"9898"
userIdIdentificador del usuario que realizo la ventaString"36142"
transTypeCdIndica el tipo de transacción que fue efecutadaString"A" - Card
"P" - Cash
transStatusEstatus de la transacción.String"C" - Approved
"D" - Rejected
"V" - Canceled
"RV" - Reversed
transDateFecha en la que se crea la transacciónString"2020-10-19T13:46:07-0500"
authCodeCódigo de autorización del bancoString"222222"
amountMonto total cobradoString"200.00"
responseCodeCódigo de respuestaString"00" - Approved
"05" - Rejected
responseMsgMensaje de respuestaString"Aprobada"
authDateFecha en la cual fue procesada la transacciónString"2020-10-19T13:46:08-0500"
userNameUsuario que realizo la ventaString"[email protected]"
tipAmountMonto de propina cobradoDouble10.0
latitudeLatitud desde donde se originó la transacciónDouble25.6714
longitudeLongitud desde donde se originó la transacciónDouble-100.309 25

10. Visualizando los detalles de una transacción

The sale detail is used to obtain more information about the sale and it is necessary to have a transactionId.

reports.getReportSalesDetail(
          binding.editTransaccionId.text.toString()
        ) {
          runOnUiThread {
            Log.w(TAG, it)
            Toast.makeText(this, it, Toast.LENGTH_SHORT).show()
          }
        }
CampoTipoDescripción
transactionIdStringIdentificador de la transacción

11. Obteniendo la respuesta de los detalles de la transacción

La respuesta será recibida en al método reportSalesByDateAndUserResult().

override fun reportSaleDetailsResult(response: ResponseSaleDetail?, success: Boolean) {
    runOnUiThread {
      if (!success) {
        binding.tvResponse.text = "Ha ocurrido un error"
        Log.d("Error Report Mensaje", response.toString())
      } else {
        val resultStr: String = Gson().toJson(response)
        binding.tvResponse.text = resultStr
      }

      Toast.makeText(this, "Proceso terminado", Toast.LENGTH_SHORT).show()
    }
  }

Ejemplo del objeto JSON recibido

{
  "contentType": "Report",
  "resourceName": "ReportSalesDetail",
  "order": {
    "orderId": "5227220201019174732079",
    "transaction": {
      "type": "A",
      "authCode": "222222",
      "transactionId": "c4f9b0d8-8867-4186-827e-ddecbb0074b0",
      "merchantId": "7489228",
      "total": {
        "total": 2.58,
        "tip": 0.0
      },
      "authDate": "2020-10-19T17:47:33-0500",
      "transStatus": "C",
      "bankName": "SANTANDER",
      "cardTypeName": "MASTERCARD",
      "cardNature": "D",
      "arqc": "3F33B0E72B90BAC5",
      "aid": "A0000000041010",
      "spanRoute": "8710",
      "web": false
    },
    "cashierId": 36142,
    "cashierName": "Farmacias Test ",
    "products": [
      {
        "id": 0,
        "amount": 1.0,
        "price": 2.58,
        "description": "Cobro directo",
        "lastUpdateTimestamp": 1559326021000
      }
    ]
  },
  "report": {},
  "response": {
    "approved": true,
    "responseCode": "00",
    "responseMsg": "Aprobada",
    "timeIn": "1603300499469",
    "timeOut": "1603300509133",
    "type": "JSON"
  }
}
CampoDescripciónTipoValor de ejemplo
merchantIdIdentificador de afiliaciónString"7489228"
bankNameNombre de la entidad bancaríaString"SANTANDER"
cardTypeNameMarca de tarjetaString"MASTERCARD"
cardNatureNaturaleza de la tarjetaString"D" - Debit
"C" - Credit
arqcARQCstring"3F33B0E72B90BAC5"
aidApplication Id.String"A0000000041010"

12. Consultando el voucher de una venta

Para consultar el voucher generado por una venta se debe contar con el orderId.

transaction.getImageVoucher(
        orderId = binding.editOrderId.text.toString()
      ) {
        runOnUiThread {
          Log.w(TAG, it)
        }
      }
CampoTipoDescripción
orderIdStringIdentificador de la orden, generado por NetPay obtenido en la respuesta

13. Obteniendo la respuesta de la consulta del voucher

La respuesta será obtenido en el método imageResult().

override fun imageResult(processed: Boolean, image: String?, message: String?) {
    runOnUiThread {
      if (processed) {
        image?.let {
          binding.tvResponse.text = image
          binding.imageVoucher.setImageBitmap(base64ToBitmap(image))
        }
      } else {
        val mesageObj = Gson().fromJson(message, ResponseErrorVoucher::class.java)
        Toast.makeText(
          this,
          "processed: $processed message: ${mesageObj.message ?: "null"}",
          Toast.LENGTH_SHORT
        ).show()
      }
    }
  }

La imágen estará codificada en un string base 64.