Skip to content

Commit 5012554

Browse files
authored
Merge pull request #59 from lunasoft/feature/SS-850
Feature/ss 850
2 parents d49fa0d + 92971ee commit 5012554

File tree

13 files changed

+420
-6
lines changed

13 files changed

+420
-6
lines changed

README.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2987,6 +2987,106 @@ public class App {
29872987
</details>
29882988

29892989
---
2990+
### **Cancelación Retenciones** ###
2991+
Servicio que permite cancelar facturas de retenciones e información de pagos con sus complementos a través de un Web Service.
2992+
<details>
2993+
<summary>Cancelación retenciones por XML</summary>
2994+
2995+
<br>
2996+
2997+
## Cancelación retenciones por XML ##
2998+
Este método recibe únicamente el XML sellado con el UUID a cancelar de retenciones e información de pagos.
2999+
3000+
3001+
**Ejemplo de consumo de la librería para cancelar retenciones con XML**
3002+
```java
3003+
import java.nio.file.Files;
3004+
import java.nio.file.Paths;
3005+
import mx.com.sw.services.cancelationretention.CancelationRetention;
3006+
import mx.com.sw.services.cancelationretention.responses.CancelationRetentionResponse;
3007+
3008+
public class App {
3009+
3010+
public static void main(String[] args)
3011+
{
3012+
try
3013+
{
3014+
//Creamos una instancia de tipo CancelationRetention
3015+
//A esta le pasamos la Url, Usuario y Contraseña para obtener el token
3016+
//Automaticamente despues de obtenerlo se procedera a Cancelar la retención
3017+
CancelationRetention cancelation = new CancelationRetention("https://services.test.sw.com.mx", "user",
3018+
"password", null, 0);
3019+
//Obtenemos el XML de cancelacion
3020+
String xmlCancelation = new String(Files.readAllBytes(Paths.get("cancelacion_retencion.xml")), "UTF-8");
3021+
CancelationRetentionResponse response = cancelation.cancelar(xmlCancelation);
3022+
3023+
if (response.getStatus().equalsIgnoreCase("success"))
3024+
{
3025+
//Acuse de cancelación
3026+
System.out.println(response.getData().getAcuse());
3027+
//Estatus del UUID
3028+
System.out.println(response.getData().getUUID());
3029+
}
3030+
else
3031+
{
3032+
//Obtenemos el detalle del Error
3033+
System.out.println("Error al cancelar");
3034+
System.out.println(response.getMessage());
3035+
System.out.println(response.getMessageDetail());
3036+
}
3037+
}
3038+
catch (Exception e)
3039+
{
3040+
System.out.println(e);
3041+
}
3042+
}
3043+
}
3044+
```
3045+
3046+
**Ejemplo de consumo de la librería para cancelar retenciones con XML utilizando token**
3047+
```java
3048+
import java.nio.file.Files;
3049+
import java.nio.file.Paths;
3050+
import mx.com.sw.services.cancelationretention.CancelationRetention;
3051+
import mx.com.sw.services.cancelationretention.responses.CancelationRetentionResponse;
3052+
3053+
public class App {
3054+
3055+
public static void main(String[] args)
3056+
{
3057+
try
3058+
{
3059+
//Creamos una instancia de tipo CancelationRetention
3060+
//A esta le pasamos la Url y el token infinito
3061+
//Este lo puede obtener ingresando al administrador de timbres con su usuario y contraseña
3062+
CancelationRetention cancelation = new CancelationRetention("https://services.test.sw.com.mx", "T2lYQ0t4L0R...", null, 0);
3063+
//Obtenemos el XML de cancelacion
3064+
String xmlCancelation = new String(Files.readAllBytes(Paths.get("cancelacion_retencion.xml")), "UTF-8");
3065+
CancelationRetentionResponse response = cancelation.cancelar(xmlCancelation);
3066+
3067+
if (response.getStatus().equalsIgnoreCase("success"))
3068+
{
3069+
//Acuse de cancelación
3070+
System.out.println(response.getData().getAcuse());
3071+
//Estatus del UUID
3072+
System.out.println(response.getData().getUUID());
3073+
}
3074+
else
3075+
{
3076+
//Obtenemos el detalle del Error
3077+
System.out.println("Error al cancelar");
3078+
System.out.println(response.getMessage());
3079+
System.out.println(response.getMessageDetail());
3080+
}
3081+
}
3082+
catch (Exception e)
3083+
{
3084+
System.out.println(e);
3085+
}
3086+
}
3087+
}
3088+
```
3089+
</details>
29903090

29913091
Para mayor referencia de un listado completo de los servicios favor de visitar el siguiente [link](http://developers.sw.com.mx/).
29923092

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<modelVersion>4.0.0</modelVersion>
44
<groupId>mx.com.sw</groupId>
55
<artifactId>sdk-java18</artifactId>
6-
<version>0.0.16.1</version>
6+
<version>0.0.17.1</version>
77
<packaging>jar</packaging>
88
<properties>
99
<maven.compiler.source>1.8</maven.compiler.source>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<?xml version="1.0" encoding="utf-8" ?><Cancelacion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Fecha="2025-08-13T10:37:18" RfcEmisor="EKU9003173C9" xmlns="http://www.sat.gob.mx/esquemas/retencionpago/1"><Folios><Folio UUID="3044cc3f-572f-4535-85e2-374c205f5b11" Motivo="02" FolioSustitucion=""/></Folios><Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI=""><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>9BJNvxbmxoUZZZInri6L3HhxeN8=</DigestValue></Reference></SignedInfo><SignatureValue>LbAI6rBtmmgH7KeJ2f0EnH19iN+Z+dubN4YjoRf7SLBZiI+wUEIEjBhQAhVU8USDRSbwxJ66/E13MiQlmeBNuwOzsFaF1l7Haf2Cn4tmrLrKKKPS36HZ4sBgc+wkInLtfMEHWuzPwEhs3oQ6z1TFpNTvFPSS0buqsVX6hHWmnbrSARtxWK/FPzC7tsSqx9WQw0WYDvL6TIauPwNdNgpOx73xensQOfX3CO8LiW8NFLt2F3O07Xde3uIeciJNUK0+/uKjbj6X0O2QFS/71tJLyBaDo984FvesKD31yZOKx0QYLS05oPb1N1CH01DTXPeP/4t9qJf7L4P9qAsVTARlow==</SignatureValue><KeyInfo><X509Data><X509Certificate>MIIFsDCCA5igAwIBAgIUMzAwMDEwMDAwMDA1MDAwMDM0MTYwDQYJKoZIhvcNAQELBQAwggErMQ8wDQYDVQQDDAZBQyBVQVQxLjAsBgNVBAoMJVNFUlZJQ0lPIERFIEFETUlOSVNUUkFDSU9OIFRSSUJVVEFSSUExGjAYBgNVBAsMEVNBVC1JRVMgQXV0aG9yaXR5MSgwJgYJKoZIhvcNAQkBFhlvc2Nhci5tYXJ0aW5lekBzYXQuZ29iLm14MR0wGwYDVQQJDBQzcmEgY2VycmFkYSBkZSBjYWxpejEOMAwGA1UEEQwFMDYzNzAxCzAJBgNVBAYTAk1YMRkwFwYDVQQIDBBDSVVEQUQgREUgTUVYSUNPMREwDwYDVQQHDAhDT1lPQUNBTjERMA8GA1UELRMIMi41LjQuNDUxJTAjBgkqhkiG9w0BCQITFnJlc3BvbnNhYmxlOiBBQ0RNQS1TQVQwHhcNMjMwNTE4MTE0MzUxWhcNMjcwNTE4MTE0MzUxWjCB1zEnMCUGA1UEAxMeRVNDVUVMQSBLRU1QRVIgVVJHQVRFIFNBIERFIENWMScwJQYDVQQpEx5FU0NVRUxBIEtFTVBFUiBVUkdBVEUgU0EgREUgQ1YxJzAlBgNVBAoTHkVTQ1VFTEEgS0VNUEVSIFVSR0FURSBTQSBERSBDVjElMCMGA1UELRMcRUtVOTAwMzE3M0M5IC8gVkFEQTgwMDkyN0RKMzEeMBwGA1UEBRMVIC8gVkFEQTgwMDkyN0hTUlNSTDA1MRMwEQYDVQQLEwpTdWN1cnNhbCAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmecO6n2GS0zL025gbHGQVxznPDICoXzR2uUngz4DqxVUC/w9cE6FxSiXm2ap8Gcjg7wmcZfm85EBaxCx/0J2u5CqnhzIoGCdhBPuhWQnIh5TLgj/X6uNquwZkKChbNe9aeFirU/JbyN7Egia9oKH9KZUsodiM/pWAH00PCtoKJ9OBcSHMq8Rqa3KKoBcfkg1ZrgueffwRLws9yOcRWLb02sDOPzGIm/jEFicVYt2Hw1qdRE5xmTZ7AGG0UHs+unkGjpCVeJ+BEBn0JPLWVvDKHZAQMj6s5Bku35+d/MyATkpOPsGT/VTnsouxekDfikJD1f7A1ZpJbqDpkJnss3vQIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDANBgkqhkiG9w0BAQsFAAOCAgEAFaUgj5PqgvJigNMgtrdXZnbPfVBbukAbW4OGnUhNrA7SRAAfv2BSGk16PI0nBOr7qF2mItmBnjgEwk+DTv8Zr7w5qp7vleC6dIsZFNJoa6ZndrE/f7KO1CYruLXr5gwEkIyGfJ9NwyIagvHHMszzyHiSZIA850fWtbqtythpAliJ2jF35M5pNS+YTkRB+T6L/c6m00ymN3q9lT1rB03YywxrLreRSFZOSrbwWfg34EJbHfbFXpCSVYdJRfiVdvHnewN0r5fUlPtR9stQHyuqewzdkyb5jTTw02D2cUfL57vlPStBj7SEi3uOWvLrsiDnnCIxRMYJ2UA2ktDKHk+zWnsDmaeleSzonv2CHW42yXYPCvWi88oE1DJNYLNkIjua7MxAnkNZbScNw01A6zbLsZ3y8G6eEYnxSTRfwjd8EP4kdiHNJftm7Z4iRU7HOVh79/lRWB+gd171s3d/mI9kte3MRy6V8MMEMCAnMboGpaooYwgAmwclI2XZCczNWXfhaWe0ZS5PmytD/GDpXzkX0oEgY9K/uYo5V77NdZbGAjmyi8cE2B2ogvyaN2XfIInrZPgEffJ4AB7kFA2mwesdLOCh0BLD9itmCve3A1FGR4+stO2ANUoiI3w3Tv2yQSg4bjeDlJ08lXaaFCLW2peEXMXjQUk7fmpb5MNuOUTW6BE=</X509Certificate><X509IssuerSerial><X509IssuerName>CN=AC UAT, O=SERVICIO DE ADMINISTRACION TRIBUTARIA, OU=SAT-IES Authority, E=oscar.martinez@sat.gob.mx, STREET=3ra cerrada de caliz, PostalCode=06370, C=MX, ST=CIUDAD DE MEXICO, L=COYOACAN, OID.2.5.4.45=2.5.4.45, OID.1.2.840.113549.1.9.2=responsable: ACDMA-SAT</X509IssuerName><X509SerialNumber>3330303031303030303030353030303033343136</X509SerialNumber></X509IssuerSerial></X509Data></KeyInfo></Signature></Cancelacion>

resources/pdfresult.pdf

0 Bytes
Binary file not shown.

src/main/java/mx/com/sw/helpers/ResponseHelper.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import mx.com.sw.services.account.info.responses.AccountListDataResponse;
1313
import mx.com.sw.services.authentication.responses.AuthenticationResponse;
1414
import mx.com.sw.services.cancelation.responses.CancelationResponse;
15+
import mx.com.sw.services.cancelationretention.responses.CancelationRetentionResponse;
1516
import mx.com.sw.services.csd.responses.CsdDataResponse;
1617
import mx.com.sw.services.csd.responses.CsdListDataResponse;
1718
import mx.com.sw.services.csd.responses.CsdResponse;
@@ -246,13 +247,22 @@ public static ResendResponse toResendResponse(Throwable ex) {
246247
return new ResendResponse(STATUS_ERROR, ex.getMessage(), getStackError(ex), null);
247248
}
248249

249-
/**
250-
* Este método obtiene una respuesta de tipo ValidateResponse.
251-
* @param ex Throwable a ser tratado
252-
* @return {@link PdfResponse}
253-
*/
250+
/**
251+
* Este método obtiene una respuesta de tipo ValidateResponse.
252+
* @param ex Throwable a ser tratado
253+
* @return {@link PdfResponse}
254+
*/
254255
public static ValidateResponse toValidateResponse(Throwable ex) {
255256
return new ValidateResponse(STATUS_ERROR, ex.getMessage(),
256257
getStackError(ex), null, null, null, null, null, null);
257258
}
259+
260+
/**
261+
* Este método obtiene una respuesta de tipo CancelationRetentionResponse.
262+
* @param ex Throwable a ser tratado
263+
* @return {@link CancelationRetentionResponse}
264+
*/
265+
public static CancelationRetentionResponse toCancelationRetentionResponse(Throwable ex) {
266+
return new CancelationRetentionResponse(STATUS_ERROR, ex.getMessage(), getStackError(ex), null);
267+
}
258268
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package mx.com.sw.services.cancelationretention;
2+
3+
import java.util.Map;
4+
import java.util.UUID;
5+
6+
import org.apache.http.client.config.RequestConfig;
7+
8+
import mx.com.sw.exceptions.ServicesException;
9+
import mx.com.sw.helpers.GeneralHelpers;
10+
import mx.com.sw.services.cancelationretention.responses.CancelationRetentionResponse;
11+
import mx.com.sw.services.cancelationretention.responses.CancelationRetentionResponseHandler;
12+
13+
/**
14+
* Servicios de cancelación de retenciones.
15+
*/
16+
public class CancelationRetention extends CancelationRetentionService {
17+
private CancelationRetentionResponseHandler handler;
18+
19+
/**
20+
* Constructor de la clase.
21+
* @param url url base de la API
22+
* @param user correo o usuario de SW
23+
* @param password password de SW.
24+
* @param proxy ip o dominio de proxy (null si no se utiliza)
25+
* @param proxyPort número de puerto de proxy (cualquier valor si proxy es null)
26+
* @throws ServicesException exception en caso de error.
27+
*/
28+
public CancelationRetention(String url, String user, String password, String proxy,
29+
int proxyPort) throws ServicesException {
30+
super(url, user, password, proxy, proxyPort);
31+
handler = new CancelationRetentionResponseHandler();
32+
}
33+
34+
/**
35+
* Constructor de la clase.
36+
* @param url url base de la API
37+
* @param token token infinito de SW.
38+
* @param proxy ip o dominio de proxy (null si no se utiliza)
39+
* @param proxyPort número de puerto de proxy (cualquier valor si proxy es null)
40+
* @throws ServicesException exception en caso de error.
41+
*/
42+
public CancelationRetention(String url, String token, String proxy, int proxyPort) throws ServicesException {
43+
super(url, token, proxy, proxyPort);
44+
handler = new CancelationRetentionResponseHandler();
45+
}
46+
47+
/**
48+
* Método de cancelación de retención enviando un XML de cancelación sellado.
49+
* <b>Nota:</b> El XML de cancelación no es igual a un CFDI de retención.
50+
* @param xmlCancelation String xml de cancelación.
51+
* @return CancelationRetentionResponse
52+
*/
53+
@Override
54+
public CancelationRetentionResponse cancelar(String xmlCancelation) {
55+
try {
56+
new CancelationRetentionValidation(getUrl(), getUser(), getPassword(), getToken())
57+
.validateRequestXML(xmlCancelation);
58+
Map<String, String> headers = getHeaders();
59+
String boundary = UUID.randomUUID().toString();
60+
String xml = String.format(
61+
"--%s\r\nContent-Disposition: form-data; name=xml;"
62+
+ "filename=xml\r\nContent-Type: text/xml\r\nContent-Transfer-Encoding: binary\r\n\r\n%s\r\n--%s--",
63+
boundary, xmlCancelation, boundary);
64+
headers.put("Content-Type", "multipart/form-data; boundary=" + boundary);
65+
RequestConfig config = GeneralHelpers.setProxyAndTimeOut(getProxy(), getProxyPort());
66+
return handler.postHTTPMultipart(getUrl(), "retencion/cancel/xml", headers, xml, config,
67+
CancelationRetentionResponse.class);
68+
} catch (ServicesException e) {
69+
return handler.handleException(e);
70+
}
71+
}
72+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package mx.com.sw.services.cancelationretention;
2+
3+
import java.util.HashMap;
4+
import java.util.Map;
5+
import mx.com.sw.exceptions.ServicesException;
6+
import mx.com.sw.services.Services;
7+
import mx.com.sw.services.cancelationretention.responses.CancelationRetentionResponse;
8+
9+
/**
10+
* Servicio para implementación de cancelación de retenciones.
11+
*/
12+
public abstract class CancelationRetentionService extends Services {
13+
14+
/**
15+
* Constructor de la clase.
16+
* @param url url base de la API
17+
* @param user correo o usuario de SW
18+
* @param password password de SW.
19+
* @param proxy ip o dominio de proxy (null si no se utiliza)
20+
* @param proxyPort número de puerto de proxy (cualquier valor si proxy es null)
21+
* @throws ServicesException exception en caso de error.
22+
*/
23+
protected CancelationRetentionService(String url, String user, String password, String proxy,
24+
int proxyPort) throws ServicesException {
25+
super(url, user, password, proxy, proxyPort);
26+
}
27+
28+
/**
29+
* Constructor de la clase.
30+
* @param url url base de la API
31+
* @param token token infinito de SW.
32+
* @param proxy ip o dominio de proxy (null si no se utiliza)
33+
* @param proxyPort número de puerto de proxy (cualquier valor si proxy es null)
34+
* @throws ServicesException exception en caso de error.
35+
*/
36+
protected CancelationRetentionService(String url, String token, String proxy, int proxyPort) throws ServicesException {
37+
super(url, token, proxy, proxyPort);
38+
}
39+
40+
/**
41+
* Método abstracto para cancelar retención mediante XML.
42+
* @param xmlCancelation String xml de cancelación.
43+
* @return CancelationRetentionResponse
44+
*/
45+
abstract CancelationRetentionResponse cancelar(String xmlCancelation);
46+
47+
/**
48+
* Obtiene los headers necesarios para el consumo del servicio.
49+
* @return Map String, String
50+
* @throws ServicesException exception en caso de error.
51+
*/
52+
protected Map<String, String> getHeaders() throws ServicesException {
53+
this.setupRequest();
54+
Map<String, String> headers = new HashMap<String, String>();
55+
headers.put("Authorization", "bearer " + this.getToken());
56+
return headers;
57+
}
58+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package mx.com.sw.services.cancelationretention;
2+
3+
import mx.com.sw.exceptions.ServicesException;
4+
import mx.com.sw.helpers.GeneralValidations;
5+
6+
/**
7+
* Clase para validaciones en el
8+
* servicio de cancelación de retenciones.
9+
*/
10+
public class CancelationRetentionValidation extends GeneralValidations {
11+
12+
/**
13+
* Constructor de la clase.
14+
* @param url url de la API Rest.
15+
* @param user usuario de SW.
16+
* @param password password de SW.
17+
* @param token token de SW.
18+
* @throws ServicesException exception en caso de error.
19+
*/
20+
public CancelationRetentionValidation(String url, String user, String password, String token) throws ServicesException {
21+
super(url, user, password, token);
22+
}
23+
24+
/**
25+
* Validación para cancelacion de retención mediante XML.
26+
* @param xml String xml de cancelación.
27+
* @throws ServicesException exception en caso de error.
28+
*/
29+
public void validateRequestXML(String xml) throws ServicesException {
30+
validateString(xml, "Faltan especificar el XML de cancelacion de retención", false, null);
31+
}
32+
33+
/**
34+
* Validación para un String.
35+
* @param value valor.
36+
* @param errorDescription mensaje de error.
37+
* @param b64 es base64?
38+
* @param parameterName nombre paramétro.
39+
* @throws ServicesException exception en caso de error.
40+
*/
41+
private void validateString(String value, String errorDescription, boolean b64, String parameterName)
42+
throws ServicesException {
43+
if (value == null || value.trim().isEmpty()) {
44+
throw new ServicesException(errorDescription);
45+
}
46+
}
47+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package mx.com.sw.services.cancelationretention.responses;
2+
3+
import java.util.Map;
4+
5+
/**
6+
* Clase con la informacion de la cancelación de retención.
7+
*/
8+
public class CancelationRetentionData {
9+
private String acuse;
10+
private Map<String, String> uuid;
11+
12+
/**
13+
* Obtiene el acuse de cancelación.
14+
* @return String
15+
*/
16+
public String getAcuse() {
17+
return this.acuse;
18+
}
19+
20+
/**
21+
* Obtiene llave valor de los UUID y su estatus de cancelación.
22+
*/
23+
public Map<String, String> getUUID() {
24+
return this.uuid;
25+
}
26+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package mx.com.sw.services.cancelationretention.responses;
2+
3+
import mx.com.sw.entities.IResponse;
4+
5+
/**
6+
* Respuesta de cancelación de retención con la información de la misma.
7+
*/
8+
public class CancelationRetentionResponse extends IResponse {
9+
private CancelationRetentionData data;
10+
11+
/**
12+
* Constructor de la clase.
13+
* @param status status de llamada a API.
14+
* @param message mensaje devuelto por API.
15+
* @param messageDetail detalles mensaje de la API.
16+
* @param data objeto con los datos de respuesta.
17+
*/
18+
public CancelationRetentionResponse(String status, String message, String messageDetail, CancelationRetentionData data) {
19+
super(status, message, messageDetail);
20+
this.data = data;
21+
}
22+
23+
/**
24+
* Obtiene los datos de la cancelación cuando es "success".
25+
* @return CancelationRetentionData
26+
*/
27+
public CancelationRetentionData getData() {
28+
return this.data;
29+
}
30+
}

0 commit comments

Comments
 (0)