Búsqueda por folio

Existe una funcionalidad por la cuál se puede obtener el ticket y el JSON de respuesta por medio de la búsqueda del folio de la venta.

1.- Pasos para implementar la búsqueda por folio.

Para realizar una búsqueda por folio, es necesario contar con la siguiente información:

  • {{serialNumber}} : Número de serie. Cadena numérica a 10 dígitos ubicada en la parte posterior de la terminal (S/N). (Obligatorio).
  • {{storeId}} : Número identificador de comercio asignado por terminal. (Obligatorio).
  • {{access_token}} : Token de acceso necesario para consumir el servicio. Ver obtener token de acceso. (Obligatorio).
  • {{folioId}} : Número de orden generado al realizar una venta. Ver información de regreso de la terminal. (Obligatorio).
  • {{orderId}} : Número de orden generado al realizar una venta. Ver información de regreso de la terminal. Este valor debe agregarse obligatoriamente pero vacío
  • {{traceability}}: objeto JSON para enviar información relevante para el comercio (Opcional).
  • {{isSmartAccounts}}: Este campo es necesario que se agregué para el uso correcto de Smart Accounts en conjunto con la implementación de la búsqueda por folio. (Obligatorio y debe estar en valor true).

📘

Nota.

  • La terminal debe tener acceso a internet en todo momento sin restricciones al dominio y subdominios *.netpay.com.mx para recibir y enviar solicitudes.
  • Debe estar encendida la pantalla de la terminal y estar en primer plano la pantalla principal de la aplicación.

2.- Estructura de la petición.

POST http://nubeqa.netpay.com.mx:3334/integration-service/transactions/reprint

Headers

Los siguientes headers deben ser enviados en la petición

  • Content-Type: application/json
  • Authorization: Bearer {{access_token}}

Body

En la petición, se debe mandar un objeto JSON con los datos según se requieran. Los valores mínimos requeridos son serialNumber, orderId (vacío), storeId, isSmartAccouts (true) y folioId.
Ejemplo:

{  
    "serialNumber": "{{serialNumber}}",
    "orderId": "",
    "folioId": "{{folioId}}"
    "storeId": "{{storeId}}"
    "isSmartAccounts": true
}

🚧

Nota.

1.- Es muy importante considerar que para que esta funcionalidad se ejecute correctamente, el orderId debe agregarse vacío ya que de lo contrario hará una búsqueda por orderId y no tomará en cuenta la búsqueda por folioId.
2.- Adicional también es importante agregar el campo isSmartAccounts con valor true, ya que es una funcionalidad que corresponde a smartAccounts.
3.- También se debe considerar que el contenido del folioId es sensible a Mayúsculas y Minúsculas por lo que les comentamos que tomen precaución al agregarlo.
4.- El folioId debe ser el correcto, si se ingresa uno incorrecto podría imprimir un ticket y traer la respuesta aleatoria.
5.- Finalmente el folioId debe ser único e irrepetible. Si se llegará a repetir entregaría la respuesta de la transacción más reciente con ese folio.

3.- Ejemplo de solicitud / respuesta.

A continuación, se muestra un ejemplo de una petición para solicitar una búsqueda por folio y los diferentes mensajes de respuesta al enviar la petición.

Endpoint

http://nubeqa.netpay.com.mx:3334/integration-service/transactions/reprint

curl --location --request POST 'http://nubeqa.netpay.com.mx:3334/integration-service/transactions/reprint' \
--header 'Authorization: Bearer {{access_token}}' \
--header 'Content-Type: application/json' \
--data-raw '{
    "folioId": "{{folioId}}",
    "orderId": "",
    "serialNumber": "{{orderId}}",
    "storeId": "{{storeId}}",
    "isSmartAccounts": true
}'
var client = new RestClient("http://nubeqa.netpay.com.mx:3334/integration-service/transactions/reprint");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer {{access_token}}");
request.AddHeader("Content-Type", "application/json");
var body = @"{
" + "\n" +
@"    ""folioId"": ""{{folioId}}"",
" + "\n" +
@"    ""orderId"": """",
" + "\n" +
@"    ""serialNumber"": ""{{serialNumber}}"",
" + "\n" +
@"    ""storeId"": ""{{storeId}}"",
" + "\n" +
@"    ""isSmartAccounts"": true
" + "\n" +
@"}";
request.AddParameter("application/json", body,  ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => 'http://nubeqa.netpay.com.mx:3334/integration-service/transactions/reprint',
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => '',
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => 'POST',
  CURLOPT_POSTFIELDS =>'{
    "folioId": "{{folioId}}",
    "orderId": "",
    "serialNumber": "{{serialNumber}}",
    "storeId": "{{storeId}}",
    "isSmartAccounts": true
}',
  CURLOPT_HTTPHEADER => array(
    'Authorization: Bearer {{access_token}}',
    'Content-Type: application/json'
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;
HttpClient client = new OkHttpClient().newBuilder()
  .build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\r\n    \"folioId\": \"{{folioId}}\",\r\n    \"orderId\": \"\",\r\n    \"serialNumber\": \"{{serialNumber}}\",\r\n    \"storeId\": \"{{storeId}}\",\r\n    \"isSmartAccounts\": true\r\n}");
Request request = new Request.Builder()
  .url("http://nubeqa.netpay.com.mx:3334/integration-service/transactions/reprint")
  .method("POST", body)
  .addHeader("Authorization", "Bearer {{access_token}}")
  .addHeader("Content-Type", "application/json")
  .build();
Response response = client.newCall(request).execute();
import requests
import json

url = "http://nubeqa.netpay.com.mx:3334/integration-service/transactions/reprint"

payload = json.dumps({
  "folioId": "{{folioId}}",
  "orderId": "",
  "serialNumber": "{{serialNumber}}",
  "storeId": "{{storeId}}",
  "isSmartAccounts": True
})
headers = {
  'Authorization': 'Bearer {{access_token}}',
  'Content-Type': 'application/json'
}

response = requests.request("POST", url, headers=headers, data=payload)

print(response.text)

Cosas a tener en cuenta.

  • Se puede emplear cualquier lenguaje de programación que se desee y permita crear una API REST.
  • El servicio debe estar funcionando siempre que se requiera realizar cobros con la terminal ya que es ahí donde se enviará la respuesta de la operación.
  • La terminal debe poder alcanzar el servicio en todo momento para poder realizar el flujo completo de una transacción.
  • En caso de que la terminal indique que no puede enviar la respuesta al servicio, se debe verificar inmediatamente si el servicio está funcionando correctamente para poder realizar el flujo correcto de una transacción.

Requisitos.

  • Al ser consumido el servicio, debe regresar un HTTP Status Code 200.
  • El verbo HTTP devuelto debe ser POST.
  • Al consumir el servicio, el body debe de contener el JSON con los siguientes parámetros. (El mensaje dentro del parámetro message puede contener cualquier texto y el parámetro code debe regresar un 00).
{
    "code": "00",
    "message": "Mensaje enviado exitosamente"
}

📘

Nota.

Recomendamos que esta funcionalidad se agregué en el POS como un nuevo módulo y no combinarlo con la reimpresión ya que tiene reglas y consideraciones muy diferentes y podría provocar conflicto.

Respuestas del envío de una búsqueda por folio.

  • SUCCESS: Se obtendrá la siguiente respuesta si la información enviada es correcta.
  • INVALID TOKEN: Se obtendrá la siguiente respuesta si el {{access_token}} enviado es incorrecto.
  • MISSING TERMINAL: Se obtendrá la siguiente respuesta si el {{serialNumber}} enviado es incorrecto.
{
    "code": "00",
    "message": "Mensaje enviado exitosamente"
}
{
    "error": "invalid_token",
    "error_description": "Cannot convert access token to JSON"
}
{
    "code": "19",
    "message": "No se encontró la terminal"
}

5.- Respuestas de regreso hacía el punto de venta

Transacción exitosa

Ejemplo de una consulta por folio de una transacción exitosa sin NIP.

{
    "affiliation": "7389145",
    "applicationLabel": "VISA ELECTRON",
    "arqc": "F952CB0CD028B6B7",
    "aid": "A0000000032022",
    "amount": "200",
    "authCode": "TEST00",
    "bankName": "BANCOPPEL",
    "bin": "",
    "cardExpDate": "02/27",
    "cardType": "D",
    "cardTypeName": "VISA",
    "cityName": "OTHON P. BLANCO,QUINTANA ROO",
    "responseCode": "00",
    "exchangeComm": "0",
    "exchangeNat": "NONE",
    "exchangeTotal": "0",
    "exchangeUnit": "0",
    "folioNumber": "EjemploFolio",
    "hexSign": "00000200000001DA0000009E000000040800031CFF02FF035300A765326D3970CA075B7EC300F1215739F054F40051C557F3983B42B0EABB8A0DDDFF0208B4AFFF003D48D46028695497CCE9779AF00AC2D57A5E4C171ABC6A468431E0CA3C1E36BF63AE0AA273467CFF02533C6830A4767D87A231F025302D7D294A0AA6D8EE313793EE1EB5A77A8B59D73F721390FF02FF02FF02FF02FF02F786C8FEE8FF02756F4DC9D5A342FF022393EF5C60FF0225CE9069E8DCFF02153AF38EE0C1C14EF045DE42EE3C996C4EA831188E2BC840F88FB369C6BE50BD8011D350F242B02F14C91B66E17BA0D8FF0200FEBB4D196AE10073396FE9BE7A979A186C15CA11396E58EA64BAC24566BE61A0FF0286ED9FEF4B42320CABF9A44AEDA2F67F5B0DC29F96AB4B5BF0509FD1E6014FEB7D94E64AFF022488006394E2C0FF025B845195FF02606F7980FF02D5207060EBD0FF02D4E5218555FF0232B00B78B8FF02DC88931F20FF022A3747C2BD4C1583AC5E800280FF02EA4DF4215ED85B29B1E57F87D9A5755735981B7375364883B03DE1FA69833F1A38A7DCE04DC8830B0A676168E9A1B5D1283C68FD26A5084E008CC0FF023E3789C4236189FF00ACD91C0F2BF9ED98F5AF086C22EF3F696DAD601014D7F930A891FD0DDBA00DF2133E162821D9FF024CF552F14477001CCD937873F62C83625C0EA88CE550ADA239A15B06B9EDDB629370FF02BBF0D1D2D6229260FF02D64707936F458EFF02ADEA0777095CFF022D160A8FF44211E74FF668FF0288BDC5FF00351677525CFF02FD90FF02EAE0FF02A0FF02D711A1E354EB6CAF7FCC58CF9EFE201E0727410F403EE0F97C2DFDE61826EF40BA35625E39A096AD5F98DF1B7C7430271E10A6745D3D206C0EFD0BA0163738564D52C0FF025EC62EA244AF63D578F716C30F6FC7E06E28F89703834B81DE3ECFFB4FF883CB469066056B84692A2ED860630540FF0240DB122F469788FF02FF02FF02",
    "internalNumber": "",
    "isQps": 0,
    "isRePrint": true,
    "message": "Transaccion Valida",
    "moduleCharge": "1",
    "moduleLote": "1",
    "customerName": "null",
    "terminalId": "1450958877",
    "orderId": "220602134739-1450958877",
    "preAuth": "0",
    "preStatus": 0,
    "promotion": "0",
    "rePrintDate": "1.3.5.p.p_20220530",
    "rePrintMark": "VISA",
    "reprintModule": "C",
    "cardNumber": "4165",
    "storeName": "EUROPIEL CHETUMAL",
    "streetName": "AV. INSURGENTES  KM 5.025",
    "tableId": "",
    "ticketDate": "JUN. 02, 22 13:47:40",
    "tipAmount": "0",
    "tipLessAmount": "0",
    "traceability": {},
    "transDate": "2022-06-02 13:47:40.0",
    "transType": "A",
    "transactionCertificate": "F952CB0CD028B6B7"
}

Otro tipo de respuestas

{
  "responseCode": "13",
  "hasPin": false,
  "internalNumber": "",
  "message": "TRANSACCION NO PERMITIDA",
  "tableId": "",
  "traceability": {}
}
{
  "responseCode": "56",
  "hasPin": false,
  "internalNumber": "",
  "message": "TARJETA INVALIDA",
  "tableId": "",
  "traceability": {}
}
{
  "responseCode": "34",
  "hasPin": false,
  "internalNumber": "",
  "message": "TARJETA RECHAZADA",
  "tableId": "",
  "traceability": {}
}
{
  "bin": "",
  "responseCode": "34",
  "folioNumber": "prueba",
  "internalNumber": "",
  "isRePrint": false,
  "message": "TRANSACCION RECHAZADA",
  "tableId": "",
  "traceability": {}
}
{
  "bin": "",
  "responseCode": "05",
  "folioNumber": "prueba",
  "internalNumber": "",
  "isRePrint": false,
  "message": "Error de conexión",
  "tableId": "",
  "traceability": {}
}
{
  "bin": "",
  "responseCode": "02",
  "folioNumber": "prueba",
  "internalNumber": "",
  "isRePrint": false,
  "message": "Cancelado por el Usuario",
  "tableId": "",
  "traceability": {}
}

Como se mencionó más arriba, se pueden obtener diferentes tipos de respuesta según el banco emisor de la tarjeta. Un responseCode diferente de 00 indica que la transacción no fue exitosa y podemos ver el motivo en el message.

📘

Nota.

Para este último punto se puede encontrar más información en el apartado de 8.- Recibiendo la respuesta