Skip to content
NovaroPayDocs
PSP-Version 2026-10-25.acacia
Theme
Merchant portal(opens in a new tab)

Payments

Send checkout with a return_url in POST /payments and the payment comes with checkout.url, our page for the payer on your account’s own domain, in your brand. The Hosted payment page guide covers the request, the way back, the iframe and the WebView cases and the page’s options.

Tell us with traffic_type in POST /payments whose deposit a payment is: ftd, the customer’s first deposit with you, or std, a subsequent one. A payment without it is ftd, so mark subsequent deposits std. The payment keeps the type for good, answers it in traffic_type (and, once they are here, in its webhooks), and GET /payments?traffic_type=std lists one type.

  • Our team can turn either type off on your account, and on again. A payment of a type turned off is 400 traffic_type_disabled with param: traffic_type, and nothing is made. With ftd off, a request without traffic_type is refused too.
  • Turning a type off stops new payments only: payments already made, and their hosted pages, carry on.
  • A value other than ftd or std, the empty one too, is 400 parameter_invalid.

In markets whose payments take one (Argentina), send the payer’s tax id in customer.tax_id of POST /payments. We find the payer by it when their transfer comes: their bank sends us the sender’s CUIT, and we compare the DNI inside it.

"customer": {
"id": "user_1234567890",
"tax_id": { "type": "ar_cuil", "value": "27-12345678-0" }
}
  • type is ar_cuit or ar_cuil, 11 digits whose last is the check digit, or ar_dni, 7 or 8 digits. value may have dashes, dots and spaces.
  • Payers often get the first two digits of a CUIT or CUIL wrong. When the check digit does not fit the number but does fit one of a person’s prefixes (20, 23, 24 or 27) for the same DNI, we take the number as that DNI instead of refusing it.
  • Any other number that is not one is 400 tax_id_invalid with param: customer.tax_id and the reason in message. A tax_id without its type or value is 400 parameter_missing, and another type 400 parameter_invalid with param: customer.tax_id.type.
  • A market whose payments do not take a tax id, such as India, answers 400 parameter_unknown for a well-formed one.
  • We keep the number encrypted and never write it to our logs.

In Argentina the payer transfers pesos from their own bank or wallet (Mercado Pago, Ualá, Brubank…) to a CVU we give, or to its alias. Create the payment with payment_method_data.type ar_bank_transfer and the payer’s tax id:

{
"amount": 1000000,
"currency": "ARS",
"merchant_reference": "dep_000123",
"payment_method_data": { "type": "ar_bank_transfer" },
"customer": {
"id": "user_1234567890",
"tax_id": { "type": "ar_cuil", "value": "27-12345678-0" }
}
}
  • The amount is whole pesos, from 2,000 to 2,000,000: a multiple of 100 centavos. One with centavos is 400 amount_not_whole with param: amount; one outside the range 400 amount_too_small or amount_too_large.
  • When you show the payer the details yourself, customer.tax_id is required: without it, 400 parameter_missing with param: customer.tax_id.
  • With checkout, our hosted page shows the transfer in Spanish. customer.tax_id is then optional: when you send none, the page asks the payer for their DNI, CUIT or CUIL before it shows where to pay. The payer copies the CVU or the alias, transfers from their bank, and taps «Ya transferí»; the page then shows that the payment is being checked, and asks for the COELSA id on the receipt only if it is not found half an hour after that; a payer who never tapped «Ya transferí» is asked once the time to pay ran out.
  • next_action is display_bank_transfer_instructions, as Stripe’s bank transfers have it:
"next_action": {
"type": "display_bank_transfer_instructions",
"display_bank_transfer_instructions": {
"type": "ar_bank_transfer",
"amount_remaining": 1000000,
"currency": "ARS",
"financial_addresses": [
{ "type": "cvu", "cvu": { "cvu": "0000009300000000000017" } },
{ "type": "alias", "alias": { "alias": "pago.tienda.ars" } }
],
"hosted_instructions_url": "https://pay.your-shop.com/c/..."
}
}
  • Show the payer the amount, the CVU and the alias: either reaches the same account. Each address is an object under its type’s name, as Stripe’s financial_addresses are, so it can carry more later; a wallet without an alias gives the CVU alone. hosted_instructions_url is our page with the same details, which you can send the payer to; it is null when your account has no page domain. There is no reference to put on the transfer. Once the payer says they paid, confirm the payment with the COELSA id on their receipt: POST /payments/{id}/confirmations with coelsa_id (1 to 64 letters, digits, dashes and underscores), as confirmation_fields names it. For now our team checks each confirmed transfer against the money that came and marks the payment paid; finding the transfer by the tax id of the account it comes from comes next.
  • We do not give the account’s holder: your payer’s app may show another name than yours, which is right.

Once a payment is succeeded it says what arrived and what you credit your customer. Each of these is null until then.

  • amount_paid: what arrived from the payer’s bank, in the smallest unit.
  • amount_credited: what to credit your customer. It is the amount you asked for when the payer paid exactly amount_payable, and amount_paid otherwise. Credit this. (amount_received is the same amount, under its first name.)
  • amount_mismatch: true when the payer paid other than amount_payable, so amount_credited is amount_paid rather than the amount you asked for.

This is how Stripe has amount_received beside amount: what arrived can differ from what you asked for.

version grows with every change of a payment. Of two copies of the same payment, the one with the higher version is the newer: a webhook event’s data.object has the version of that event, so an event whose version is lower than one you already processed is an older change, and you can ignore it.

When your payer tells you they paid and gives you the transaction’s reference (in India, the 12-digit UTR their UPI app shows), you can pass it on with POST /payments/{id}/confirmations. It is optional: we match payments without it. We keep what you send with the payment, as your payer’s word, for our checks and our team. A payment’s confirmation_fields name the fields your market takes and the pattern each must match.

  • A confirmation is not proof. A payment waiting for the payer is processing after it and stays processing while we look for its money: it succeeds when we see it, and fails if we have not seen it 24 hours after your confirmation.
  • The answer is 200 with the payment, whether or not its money arrived yet: a payment that succeeds comes as the webhook event payment.succeeded. The same value again on the same payment answers it again and records nothing new, and so does the reference of the bank transaction that paid it, after it succeeded too; another value is kept beside the first.
  • A UTR is one UPI transaction, so it confirms one payment: a value already confirmed on another payment, or one that paid another payment, is 409 reference_already_used. One that does not match the field’s pattern is 400 parameter_invalid.
  • A payment takes at most 3 different values: more is 400 parameter_invalid. A value holds only while its payment is open: once that payment succeeded or failed, the value can confirm another payment.
  • An expired payment takes the confirmation and stays expired: if the money arrives in time, it still succeeds. A payment that succeeded or failed refuses one: 409 payment_final.
  • A method with no confirmation_fields takes none: 400 confirmation_not_supported.

Endpoints

  • GET /paymentsListPayments
  • POST /paymentsCreatePayment
  • GET /payments/{id}GetPayment
  • POST /payments/{id}/confirmationsConfirmPayment

Loading the reference…