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 reimpresión 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
- {{disablePrintAnimation}} : Bandera de control para activar/desactivar ticket de venta. Esta bandera es opcional, si se incluye se debe enviar obligatoriamente el valor true o false.
- {{traceability}}: objeto JSON para enviar información relevante para el comercio (Opcional).
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 https://api-154.api-netpay.com/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 y folioId.
Ejemplo:
{
"serialNumber": "{{serialNumber}}",
"orderId": "", // Debe ser vacío para que tome en cuenta el folioId
"folioId": "{{folioId}}",
"storeId": "{{storeId}}",
"disablePrintAnimation": false
}
{
"serialNumber": "{{serialNumber}}",
"orderId": "",
"folioId": "{{folioId}}",
"storeId": "{{storeId}}",
"disablePrintAnimation": 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.- 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.
3.- El folioId debe ser el correcto, si se ingresa uno incorrecto podría imprimir un ticket y traer la respuesta aleatoria.
4.- 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
https://api-154.api-netpay.com/integration-service/transactions/reprint
curl --location --request POST 'https://api-154.api-netpay.com/integration-service/transactions/reprint' \
--header 'Authorization: Bearer {{access_token}}' \
--header 'Content-Type: application/json' \
--data-raw '{
"folioId": "{{folioId}}",
"orderId": "",
"serialNumber": "{{orderId}}",
"storeId": "{{storeId}}"
}'
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" +
@"}";
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}}"
}',
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}}\");
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}}"
})
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"
}
4 .- 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
Este servicio de reimpresión por folio que depende de la terminal puede ayudar a conocer el estatus de los reversos, que son transacciones que por corte de comunicación a internet o retiro de tarjeta durante el procesamiento puede generar incongruencias en los estatus de una transacción. Próximamente se anexará documentación más específica.