Enviar una remesa con un monto de recepción fijo
Un pago de remesa es una transferencia de dinero de una persona a otra, normalmente entre países o a larga distancia, que a menudo implica una conversión de monedas. En esta guía, aprenderá cómo implementar una funcionalidad de pago único de remesa en la que su usuario pueda especificar exactamente cuánto debe recibir el destinatario.
Este enfoque es útil en particular para situaciones de aplicaciones de remesas en estos casos:
- El remitente y el destinatario efectúan operaciones, cada uno, en monedas distintas.
- El remitente busca que el destinatario reciba un monto fijo expresado en la moneda local del destinatario.
- El emisor está dispuesto a asumir cualquier diferencia generada por el tipo de cambio.
Caso hipotético
Sección titulada «Caso hipotético»Imagine que alguien en EE. UU. quiere enviar dinero a un familiar en México. Esta persona quiere que su familiar reciba exactamente 5000 pesos mexicanos (MXN), independientemente de cuál sea la conversión de monedas. Esto difiere de un pago en el que el remitente especifica exactamente cuánto enviar, y el monto que el destinatario recibe puede variar con el tipo de cambio.
Para esta guía, asumirá el papel de un desarrollador que está creando una aplicación de remesas. La guía explica la forma de enviar un pago en USD, en la que el destinatario recibe exactamente MXN 5000.
Detalles de la transacción de ejemplo:
- El destinatario recibe: MXN 5000 (el monto exacto)
- Conversión de moneda: USD a MXN a un tipo de cambio de 18,00
- El remitente envía: USD $277,78 ($5000/18,00)
Las tres partes que intervienen en este caso son las siguientes:
- Desarrollador: usted, la persona que construye la aplicación de remesas.
- Remitente: la persona que utiliza su aplicación para enviar dinero en USD.
- Destinatario: la persona que recibe el dinero en MXN.
Puntos finales
Sección titulada «Puntos finales»- GET Get Wallet Address
- POST Grant Request
- POST Create Incoming Payment
- POST Create a Quote
- POST Grant Continuation Request
- POST Create an Outgoing Payment
1. Obtener los datos de la dirección de billetera
Sección titulada «1. Obtener los datos de la dirección de billetera»Cuando el remitente inicia el pago de la remesa, su aplicación necesita obtener la información de la dirección de billetera tanto del remitente como del destinatario.
Asumamos que el remitente ya ha proporcionado su propia dirección de billetera cuando se registró en su aplicación. Asumamos también que el remitente ingresó la dirección de billetera del destinatario en el formulario de pago de su aplicación.
Llame a la GET Get Wallet Address API para cada dirección.
const senderWalletAddress = await client.walletAddress.get({ url: 'https://cloudninebank.example.com/sender'})const recipientWalletAddress = await client.walletAddress.get({ url: 'https://happylifebank.example.com/recipient'})Respuestas de ejemplo
El siguiente ejemplo muestra una respuesta del proveedor de billetera del remitente.
{ "id": "https://cloudninebank.example.com/sender", "assetCode": "USD", "assetScale": 2, "authServer": "https://auth.cloudninebank.example.com/", "resourceServer": "https://cloudninebank.example.com/op"}El siguiente ejemplo muestra una respuesta del proveedor de billetera del destinatario.
{ "id": "https://happylifebank.example.com/recipient", "assetCode": "MXN", "assetScale": 2, "authServer": "https://auth.happylifebank.example.com/", "resourceServer": "https://happylifebank.example.com/op"}2. Solicitar una concesión de autorización para un pago entrante
Sección titulada «2. Solicitar una concesión de autorización para un pago entrante»Utilice los datos del destinatario authServer, recibidos en el paso anterior, para llamar a la POST Grant Request API.
Esta llamada obtiene un token de acceso que permite a su aplicación solicitar la creación de un recurso de pago entrante en la cuenta de billetera del destinatario.
const recipientIncomingPaymentGrant = await client.grant.request( { url: recipientWalletAddress.authServer }, { access_token: { access: [ { type: 'incoming-payment', actions: ['create'] }, ], }, },);Ejemplo de respuesta
El siguiente ejemplo muestra una respuesta del proveedor de billetera del destinatario.
{ "access_token": { "value": "...", // access token value for incoming payment grant "manage": "https://auth.happylifebank.example.com/token/{...}", // management uri for access token "access": [ { "type": "incoming-payment", "actions": ["create"] } ] }, "continue": { "access_token": { "value": "..." // access token for continuing the request }, "uri": "https://auth.happylifebank.example.com/continue/{...}" // continuation request uri }}3. Solicitar la creación de un recurso de pago entrante
Sección titulada «3. Solicitar la creación de un recurso de pago entrante»Use el token de acceso devuelto en la respuesta anterior para llamar a la POST Create Incoming Payment API.
Esta llamada solicita la creación de un recurso de pago entrante en la cuenta de billetera del destinatario.
const recipientIncomingPayment = await client.incomingPayment.create( { url: recipientWalletAddress.resourceServer, accessToken: recipientIncomingPaymentGrant.access_token.value }, { walletAddress: recipientWalletAddress.id, },)Ejemplo de respuesta
El siguiente ejemplo muestra una respuesta del proveedor de billetera del destinatario.
{ "id": "https://happylifebank.example.com/incoming-payments/{...}", "walletAddress": "https://happylifebank.example.com/recipient", "receivedAmount": { "value": "0", "assetCode": "MXN", "assetScale": 2 }, "completed": false, "createdAt": "2025-03-12T23:20:50.52Z", "methods": [ { "type": "ilp", "ilpAddress": "...", "sharedSecret": "..." } ]}4. Solicitar una concesión de autorización para una cotización
Sección titulada «4. Solicitar una concesión de autorización para una cotización»Utilice los datos del remitente authServer recibidos en el paso 1 para llamar a la POST Grant Request API.
Esta llamada obtiene un token de acceso que permite a su aplicación solicitar la creación de un recurso de cotización en la cuenta de billetera del remitente.
const senderQuoteGrant = await client.grant.request( { url: senderWalletAddress.authServer }, { access_token: { access: [ { type: 'quote', actions: ['create'] } ] } })Ejemplo de respuesta
El siguiente ejemplo muestra una respuesta del proveedor de billetera del remitente.
{ "access_token": { "value": "...", // access token value for quote grant "manage": "https://auth.cloudninebank.example.com/token/{...}", // management uri for access token "access": [ { "type": "quote", "actions": ["create"] } ] }, "continue": { "access_token": { "value": "..." // access token for continuing the request }, "uri": "https://auth.cloudninebank.example.com/continue/{...}" // continuation request uri }}5. Solicitar la creación de un recurso de cotización
Sección titulada «5. Solicitar la creación de un recurso de cotización»Utilice el token de acceso recibido en el paso anterior para llamar a la POST Create Quote API.
Esta llamada solicita la creación de un recurso de cotización en la cuenta de la billetera del remitente. La solicitud debe contener el receiver, que es la id de pago entrante del destinatario, junto con el receiveAmount, que es el monto exacto que el remitente quiere que reciba el destinatario.
El receiveAmount especifica que el destinatario recibirá exactamente MXN 5000.
const senderQuote = await client.quote.create( { url: senderWalletAddress.resourceServer, accessToken: senderQuoteGrant.access_token.value }, { method: 'ilp', walletAddress: senderWalletAddress.id, receiver: recipientIncomingPayment.id, receiveAmount: { value: '500000', assetCode: 'MXN', assetScale: 2 } })La respuesta devuelve un receiveAmount, un debitAmount y demás información requerida.
debitAmount: el monto que el remitente debe pagar (en USD en nuestro ejemplo) después de la conversión de moneda.receiveAmount: el monto que el destinatario recibirá realmente (exactamente MXN 5000 en nuestro ejemplo).
Usted usará este mismo receiveAmount en el paso siguiente al solicitar la concesión de autorización del pago saliente, de modo que el remitente autorice este monto exacto.
Ejemplo de respuesta
El siguiente ejemplo muestra una respuesta del proveedor de billetera del remitente.
{ "id": "https://cloudninebank.example.com/quotes/{...}", // url identifying the quote "walletAddress": "https://cloudninebank.example.com/sender", "receiver": "https://happylifebank.example.com/incoming-payments/{...}", // url of the incoming payment the quote is created for "debitAmount": { "value": "27778", // Sender pays $277.78 USD after currency conversion "assetCode": "USD", "assetScale": 2 }, "receiveAmount": { "value": "500000", // Recipient receives $5,000 MXN "assetCode": "MXN", "assetScale": 2 }, "method": "ilp", "createdAt": "2025-03-12T23:22:51.50Z", "expiresAt": "2025-03-12T23:24:51.50Z"}6. Solicitar una concesión de autorización interactiva para un pago saliente
Sección titulada «6. Solicitar una concesión de autorización interactiva para un pago saliente»Utilice la información del remitente authServer recibida en el paso 1 para llamar a la POST Grant Request API.
Esta llamada obtiene un token de acceso que permite a su aplicación solicitar la creación de un recurso de pago saliente en la cuenta de billetera del remitente.
Para asegurar que el remitente autorice el monto correcto, incluya el receiveAmount en el objeto limits. Esto limita el pago saliente al monto de recepción especificado y mantiene la concesión de autorización alineada con la cotización del paso anterior.
const pendingSenderOutgoingPaymentGrant = await client.grant.request({ url: senderWalletAddress.authServer},{ access_token: { access: [ { identifier: senderWalletAddress.id, type: 'outgoing-payment', actions: ['create'], limits: { receiveAmount: { assetCode: 'MXN', assetScale: 2, value: '500000' } } } ] }, interact: { start: ['redirect'], finish: { method: 'redirect', uri: 'https://myapp.example.com/finish/{...}', // where to redirect your user after they've completed the interaction nonce: NONCE } }})Ejemplo de respuesta
El siguiente ejemplo muestra una respuesta del proveedor de billetera del remitente.
{ "interact": { "redirect": "https://auth.interledger-test.dev/{...}", // uri to redirect the customer to, to begin interaction "finish": "..." // unique key to secure the callback }, "continue": { "access_token": { "value": "..." // access token for continuing the outgoing payment grant request }, "uri": "https://auth.interledger-test.dev/continue/{...}", // uri for continuing the outgoing payment grant request "wait": 30 }}7. Comenzar la interacción con el consumidor
Sección titulada «7. Comenzar la interacción con el consumidor»Once the client receives the authorization server’s response, it must send the user to the interact.redirect URI contained in the response. This starts the interaction flow.
The response also includes a continue object, which is essential for managing the interaction and obtaining explicit user consent for outgoing payment grants. The continue object contains an access token and a URI that the client will use to finalize the grant request after the user has completed their interaction with the identity provider (IdP). This ensures that the client can securely obtain the necessary permissions to proceed with the payment process.
8. Finalizar la interacción con el consumidor
Sección titulada «8. Finalizar la interacción con el consumidor»The user interacts with the authorization server through the server’s interface and approves or denies the grant.
Provided the user approves the grant, the authorization server:
- Sends the user to the
finish.uriprovided in the interactive outgoing payment grant request. The means by which the server sends the user to the URI is out of scope, but common options include redirecting the user from a web page and launching the system browser with the target URI. - Secures the redirect by adding a unique hash, allowing your client to validate the
finishcall, and an interaction reference as query parameters to the URI.
9. Solicitar una continuación de la concesión de autorización
Sección titulada «9. Solicitar una continuación de la concesión de autorización»En nuestro ejemplo, suponemos que el IdP con el que interactuó su usuario cuenta con una interfaz de usuario. Cuando la interacción finaliza, el usuario regresa a su aplicación. Ahora su aplicación puede realizar una solicitud de continuación de la concesión de autorización de pago saliente.
Llame a la POST Grant Continuation Request API. Esta llamada solicita un token de acceso que permite a su aplicación solicitar la creación de un recurso de pago saliente en la cuenta de billetera del remitente.
Emita la solicitud al continue.uri proporcionado en la respuesta de concesión de autorización de pago saliente inicial (paso 6).
Incluya la interact_ref devuelta en los parámetros de consulta del URI de redirección.
const senderOutgoingPaymentGrant = await client.grant.continue( { url: pendingSenderOutgoingPaymentGrant.continue.uri, accessToken: pendingSenderOutgoingPaymentGrant.continue.access_token.value }, { interact_ref: interactRef })Ejemplo de respuesta
El siguiente ejemplo muestra una respuesta del proveedor de billetera del remitente.
{ "access_token": { "value": "...", // final access token required before creating outgoing payments "manage": "https://auth.cloudninebank.example.com/token/{...}", // management uri for access token "access": [ { "type": "outgoing-payment", "actions": ["create"], "identifier": "https://cloudninebank.example.com/sender", "limits": { "receiver": "https://happylifebank.example.com/incoming-payments/{...}" // url of the incoming payment that's being paid } } ] }, "continue": { "access_token": { "value": "..." // access token for continuing the request }, "uri": "https://auth.cloudninebank.example.com/continue/{...}" // continuation request uri }}10. Solicitar la creación de un recurso de pago saliente
Sección titulada «10. Solicitar la creación de un recurso de pago saliente»Utilice el token de acceso devuelto en el paso 9 para llamar a la POST Create Outgoing Payment API. Incluya el quoteId en la solicitud. La quoteId es la id devuelta en la respuesta de API Crear cotización (paso 5).
const senderOutgoingPayment = await client.outgoingPayment.create( { url: senderWalletAddress.resourceServer, accessToken: senderOutgoingPaymentGrant.access_token.value }, { walletAddress: senderWalletAddress.id, quoteId: senderQuote.id })Si la solicitud falla porque la cotización expiró, solicite una nueva e intente otra vez. Si falla porque el token de acceso de la concesión de autorización ha expirado, llame a POST Rotate Access Token API para obtener un nuevo token de acceso y, luego, vuelva a probar la solicitud con el nuevo token.
Ejemplo de respuesta
El siguiente ejemplo muestra una respuesta del proveedor de billetera del remitente.
{ "id": "https://cloudninebank.example.com/outgoing-payments/{...}", // url identifying the outgoing payment "walletAddress": "https://cloudninebank.example.com/sender", "receiver": "https://happylifebank.example.com/incoming-payments/{...}", // url of the incoming payment being paid "debitAmount": { "value": "27778", // Sender pays $277.78 USD after currency conversion "assetCode": "USD", "assetScale": 2 }, "receiveAmount": { "value": "500000", // Recipient receives $5,000 MXN "assetCode": "MXN", "assetScale": 2 }, "sentAmount": { "value": "0", "assetCode": "USD", "assetScale": 2 }, "createdAt": "2025-03-12T23:20:54.52Z"}