Órdenes
Una Order es el pago en sí — referencia una
cotización, lleva el emisor y el receptor, y atraviesa los estados
descritos en estados de una order hasta que se
completa o falla (ver detalles de falla).
OrdersClient cubre crear, leer y listar órdenes.
Accede a estos métodos directamente en RetornaClient o a través de
client.orders() — las dos formas ejecutan el mismo código.
createOrder(request, idempotencyKey)
Crea una orden contra una cotización. El backend requiere un header
X-Idempotency-Key en cada llamada; el SDK lo envía por ti a partir del argumento
idempotencyKey.
Firma
public Order createOrder(CreateOrderRequest request)
public Order createOrder(CreateOrderRequest request, String idempotencyKey)
La sobrecarga de un solo argumento genera un UUID al azar como clave de idempotencia.
Prefiere la forma de dos argumentos con tu propia referencia (un número de factura, por
ejemplo) — ese valor vuelve como order.idempotencyKey(), así que puedes buscar la orden
después por ese valor, y un reintento que reutiliza la misma clave contra el mismo
cuerpo repite la orden original en lugar de crear un duplicado.
Parámetros
| Nombre | Tipo | Requerido | Descripción |
|---|---|---|---|
request | CreateOrderRequest | Sí | La orden a crear. Ver los componentes abajo. |
idempotencyKey | String | No | Tu propia referencia. Si es null o está vacía, se genera un UUID al azar. Reutilizar la misma clave con un cuerpo distinto es un 409; reutilizarla con el cuerpo idéntico repite la orden original. |
Componentes de CreateOrderRequest — ármalo con el constructor canónico o
withPurpose(...):
// Canonical (5-arg) constructor
public CreateOrderRequest(
String quotationId,
Map<String, Object> metadata,
String purpose,
OrderSender sender,
OrderDestination destination
)
// Typed entry point for the purpose catalog
public static CreateOrderRequest withPurpose(
String quotationId,
Map<String, Object> metadata,
OrderPurpose purpose,
OrderSender sender,
OrderDestination destination
)
| Componente | Tipo | Requerido | Descripción |
|---|---|---|---|
quotationId | String | Sí | UUID de una cotización creada previamente. De un solo uso: respalda exactamente una orden, para siempre. |
metadata | Map<String, Object> | No | Etiquetas definidas por el cliente, devueltas sin cambios. Se omite del wire cuando es null. |
purpose | String (o OrderPurpose vía withPurpose) | Sí | Propósito declarado de la remesa. Tiene que ser un valor de OrderPurpose — null o cualquier valor fuera del catálogo lanza una excepción del lado del cliente. No hay un campo idempotencyKey aquí; es solo de respuesta. |
sender | OrderSender | Sí | names, lastNames, document (PersonDocument), country (ISO alpha-2 de 2 caracteres), phone (nullable, E.164), email (nullable). |
destination | OrderDestination | Sí | Ver abajo. |
OrderSender es un record: OrderSender(names, lastNames, document, country, phone, email). El
constructor anterior de 4 argumentos (names, lastNames, document, country) sigue funcionando —
phone y email quedan en null por defecto — así que el código existente compila sin cambios.
Usa la forma de 6 argumentos para definir phone y/o email; el cliente valida phone como
E.164 y email como dirección cuando no son null. Los dos vuelven en sender() en las lecturas
solo si los enviaste al crear.
OrderDestination — para construir un request, usa el constructor de 2 argumentos
(receiver, paymentInstructions); targetAmount/targetCurrency son solo de
respuesta y quedan afuera del wire de creación.
| Componente | Tipo | Requerido | Descripción |
|---|---|---|---|
receiver | OrderReceiver | Sí | names, lastNames, document (PersonDocument), email (nullable), country (ISO alpha-2 de 2 caracteres). |
paymentInstructions | PaymentInstructions | Sí | Exactamente uno de bankAccount o bankPhoneAccount tiene que estar definido — constrúyelo con PaymentInstructions.ofBankAccount(...) o PaymentInstructions.ofBankPhoneAccount(...). |
PersonDocument(type, id, country) — type (por ejemplo "CC", "V"),
id (número de documento), country (ISO alpha-2 de 2 caracteres del país emisor).
Para un pago por transferencia bancaria, construye la cuenta con BankAccount.of(FinancialEntity, number, BankAccountType):
public static BankAccount of(FinancialEntity bank, String number, BankAccountType type)
| Componente | Tipo | Requerido | Descripción |
|---|---|---|---|
bank | FinancialEntity | Sí | Código de plataforma del catálogo cerrado de bancos, por ejemplo BANCO_BANESCO_VE — nunca el nombre comercial. Consulta entidades bancarias para la lista completa. |
number | String | Sí | Número de cuenta, guardado como string para preservar ceros a la izquierda. |
type | BankAccountType | Sí | CHECKING, SAVINGS, CORRIENTE, o AHORRO — el backend acepta tanto la forma en inglés como en español. |
Retorno
Order:
| Accesor | Tipo | Significado |
|---|---|---|
id() | String | Id de la orden. |
type() | String | Tipo de orden (por ejemplo "REMITTANCE"). |
status() | String | Ver estados de una order para los valores observados y cómo evolucionan. |
quotationId() | String | La cotización con la que se creó esta orden. |
idempotencyKey() | String | Tu clave de idempotencia, devuelta sin cambios; nullable. |
amount() / currency() | String | Monto y código de moneda de origen. |
metadata() | Map<String, Object> | Metadata devuelta sin cambios; nullable. |
sender() / destination() | OrderSender / OrderDestination | Nullable en algunos estados. destination().targetAmount()/targetCurrency() llevan el monto del lado del pago en las lecturas. |
execution() | OrderExecution | source() y destination(), cada uno un ExecutionLeg con status() y referenceId() (nullable). |
purposeEnum() | OrderPurpose | Parseado del string crudo purpose(); null si está ausente o no lo reconoce esta versión del SDK. |
events() | List<OrderEvent> | Línea de tiempo de cambios de estado; nullable. Cada OrderEvent tiene fromStatus() (nullable), toStatus(), trigger(), reason() (nullable), message() (nullable), createdAt(). |
failureCode() / failureReason() | String | Detalle de falla legible por máquina / por humanos; ambos nullable. Ver detalles de falla. Ramifica según failureCode(), nunca según failureReason(). |
createdAt() / updatedAt() | OffsetDateTime | Timestamps. |
El JSON en el wire es snake_case (quotation_id, idempotency_key, failure_code, ...);
los accesores de Java de arriba son camelCase.
Ejemplo
import app.retorna.sdk.RetornaClient;
import app.retorna.sdk.RetornaClientBuilder;
import app.retorna.sdk.core.environment.RetornaEnvironment;
import app.retorna.sdk.models.order.BankPhoneAccount;
import app.retorna.sdk.models.order.CreateOrderRequest;
import app.retorna.sdk.models.order.FinancialEntity;
import app.retorna.sdk.models.order.Order;
import app.retorna.sdk.models.order.OrderDestination;
import app.retorna.sdk.models.order.OrderPurpose;
import app.retorna.sdk.models.order.OrderReceiver;
import app.retorna.sdk.models.order.OrderSender;
import app.retorna.sdk.models.order.PaymentInstructions;
import app.retorna.sdk.models.order.PersonDocument;
RetornaClient client = new RetornaClientBuilder()
.environment(RetornaEnvironment.SANDBOX)
.clientId(System.getenv("RETORNA_CLIENT_ID"))
.clientSecret(System.getenv("RETORNA_CLIENT_SECRET"))
.privateKey(System.getenv("RETORNA_PRIVATE_KEY"))
.buildClient();
String quotationId = "0190f9b2-0000-7000-8000-000000000000"; // from createQuotation
String idempotencyKey = "invoice-2026-0042"; // your own reference
Order order = client.createOrder(
CreateOrderRequest.withPurpose(
quotationId,
null,
OrderPurpose.FAMILY_SUPPORT,
new OrderSender("Ana María", "Pérez Gómez",
new PersonDocument("CC", "1020304050", "CO"), "CO"),
new OrderDestination(
new OrderReceiver("Carlos Andrés", "Rodríguez López",
new PersonDocument("V", "12345678", "VE"),
"[email protected]", "VE"),
PaymentInstructions.ofBankPhoneAccount(
BankPhoneAccount.of(FinancialEntity.BNC_VE,
"+584121234567", "12345678")))),
idempotencyKey);
System.out.printf("Order %s status=%s idempotencyKey=%s%n",
order.id(), order.status(), order.idempotencyKey());
Errores
IllegalArgumentException— se lanza antes de que salga cualquier request, por unquotationIdmal formado (no es un UUID), unpurposenull o no reconocido, campos faltantes de sender/receiver, un email de receiver que no cumple una validación básica de forma, unsender.phoneno null que no es E.164, unsender.emailno null que no cumple una validación básica de forma, o instrucciones de pago que definen los dos o ninguno debankAccount/bankPhoneAccount.RetornaB2BExceptionconcodeEnum()uno de:B2B_QUOTATION_NOT_FOUND,B2B_QUOTATION_EXPIRED,B2B_QUOTATION_MISMATCH,B2B_QUOTATION_ALREADY_USED— lee la nota en esa página antes de reintentar nada,B2B_INSUFFICIENT_BALANCE,B2B_BENEFICIARY_INVALID,B2B_IDEMPOTENCY_CONFLICT— misma clave, cuerpo distinto,B2B_UNAUTHORIZED,B2B_RATE_LIMITED— reintentable con la misma clave,B2B_PROVIDER_REJECTEDoB2B_PROVIDER_UNAVAILABLE— este último reintentable,B2B_UNKNOWN. Usae.codeEnum().isRetryable()para decidir, y siempre reintentacreateOrdercon la mismaidempotencyKey.RetornaAuthException— falla del intercambio de token.RetornaSdkException— falla de red o falla de deserialización.
Notas
- Pasa siempre tu propio
idempotencyKeyen código de producción; la sobrecarga de un solo argumento es cómoda para scripts, no para algo que necesites reintentar de forma segura. - No hay un campo
idempotencyKeyen el request — solo en la respuesta. No intentes mandar uno; siempre sale de la clave de idempotencia.
Referencia de API
getOrder(id)
Lee una única orden por id.
Firma
public Order getOrder(String id)
Parámetros
| Nombre | Tipo | Requerido | Descripción |
|---|---|---|---|
id | String | Sí | El id de la orden tal como lo emitió el backend — se valida solo por longitud (1 a 100 caracteres), no como forma de UUID. |
Retorno
La misma forma de Order devuelta por createOrder arriba.
Ejemplo
import app.retorna.sdk.RetornaClient;
import app.retorna.sdk.RetornaClientBuilder;
import app.retorna.sdk.core.environment.RetornaEnvironment;
import app.retorna.sdk.models.order.Order;
RetornaClient client = new RetornaClientBuilder()
.environment(RetornaEnvironment.SANDBOX)
.clientId(System.getenv("RETORNA_CLIENT_ID"))
.clientSecret(System.getenv("RETORNA_CLIENT_SECRET"))
.privateKey(System.getenv("RETORNA_PRIVATE_KEY"))
.buildClient();
Order order = client.getOrder("0190f9b2-0000-7000-8000-000000000001");
System.out.printf("status=%s failureCode=%s%n", order.status(), order.failureCode());
Errores
IllegalArgumentException—ides null, vacío, o más largo que 100 caracteres.RetornaB2BExceptionconcodeEnum()uno de:B2B_ORDER_NOT_FOUND,B2B_UNAUTHORIZED,B2B_RATE_LIMITED— reintentable,B2B_UNKNOWN.RetornaAuthException— falla del intercambio de token.RetornaSdkException— falla de red o falla de deserialización.
Notas
- El id es opaco; no asumas una forma fija.
Referencia de API
listOrders(params)
Lista órdenes, paginadas por cursor, con filtros opcionales.
Firma
public OrdersListResponse listOrders(ListOrdersParams params)
Parámetros
| Nombre | Tipo | Requerido | Descripción |
|---|---|---|---|
params | ListOrdersParams | No (pasa null o ListOrdersParams.firstPage()) | Filtros de la consulta. Ver los componentes abajo. |
Componentes de ListOrdersParams:
| Componente | Tipo | Requerido | Descripción |
|---|---|---|---|
cursor | String | No | Cursor opaco de next_cursor de la página anterior. Null para la primera página. |
limit | Integer | No | Tamaño de página, 1 a 100; el default del backend es 20. Mantenlo fijo durante todo el recorrido. |
status | String | No | Filtra por estado de la orden. Libre — no está restringido a un enum. |
idempotencyKey | String | No | Filtra por tu clave de idempotencia. Coincidencia exacta, no parcial. |
createdFrom / createdTo | OffsetDateTime | No | Límites inclusivos sobre created_at. |
type | String | No | Filtra por tipo de transacción, ej. "WALLET_TO_POG". Libre, un solo valor — a diferencia de status, no acepta una lista separada por comas. Se agregó como último componente de ListOrdersParams; el constructor anterior de 6 argumentos sigue funcionando y deja type en null. |
Factories de conveniencia: ListOrdersParams.firstPage(), firstPage(int limit),
byIdempotencyKey(String idempotencyKey), y byType(String type).
Retorno
OrdersListResponse:
| Accesor | Tipo | Significado |
|---|---|---|
data() | List<Order> | Órdenes de esta página — ver la forma de Order de createOrder arriba. |
pagination() | CursorPagination | nextCursor() (String, nullable cuando termina), hasMore() (boolean), limit() (int), total() (Integer, nullable — cuenta de coincidencias, no tamaño de página). |
Ejemplo
import app.retorna.sdk.RetornaClient;
import app.retorna.sdk.RetornaClientBuilder;
import app.retorna.sdk.core.environment.RetornaEnvironment;
import app.retorna.sdk.models.order.ListOrdersParams;
import app.retorna.sdk.models.order.Order;
import app.retorna.sdk.models.order.OrdersListResponse;
RetornaClient client = new RetornaClientBuilder()
.environment(RetornaEnvironment.SANDBOX)
.clientId(System.getenv("RETORNA_CLIENT_ID"))
.clientSecret(System.getenv("RETORNA_CLIENT_SECRET"))
.privateKey(System.getenv("RETORNA_PRIVATE_KEY"))
.buildClient();
String cursor = null;
do {
OrdersListResponse page = client.listOrders(
cursor == null ? ListOrdersParams.firstPage(20)
: new ListOrdersParams(cursor, 20, null, null, null, null));
for (Order order : page.data()) {
System.out.printf("%s %s %s%n", order.id(), order.status(), order.amount());
}
cursor = page.pagination().nextCursor();
} while (cursor != null);
Errores
IllegalArgumentException— uncursoroidempotencyKeyvacío (pero no null), o unlimitfuera de 1 a 100.RetornaB2BExceptionconcodeEnum()uno de:B2B_INVALID_PAGINATION_CURSOR— reinicia el recorrido sin cursor,B2B_UNAUTHORIZED,B2B_RATE_LIMITED— reintentable,B2B_UNKNOWN.RetornaAuthException— falla del intercambio de token.RetornaSdkException— falla de red o falla de deserialización.
Notas
- Consulta paginación para el contrato completo del cursor:
mantén
limity los filtros fijos durante todo el recorrido, y trata el cursor como opaco. idempotencyKeyes una coincidencia exacta sobre tu clave de idempotencia — muchas veces más rápido que paginar cuando ya sabes qué orden estás buscando.