Skip to main content

Payment intents

A payment intent guides the lifecycle of a payment. Use exactly one intent per payment to keep traceability clear.

Basic flow

  1. Create or collect a payment method for the buyer.
  2. Create a payment intent with the amount and currency.
  3. Confirm the intent using the paymentMethodId.
  4. Listen for webhooks before marking the payment as complete in your system.

Create a payment method

Create the payment method client-side with a publishable key. For cards, the response returns an id that you later send as paymentMethodId when confirming the intent from your server.

PCI scope

To reduce PCI scope, collect card data client-side with a publishable key, Checkout, or the web SDK. Do not pass untokenized card data through your server. If your backend receives or transmits PAN, CVV, or full card data, that integration is in your PCI scope and may require SAQ D validation.

Peru merchant account requirement

important

For merchant accounts created in Peru, the customer attached to the payment method must have an email address when creating payment methods.

You can meet this requirement in two ways:

  • Send customer.email in the payment method creation payload to create and attach the customer in the same request.
  • Send the customerId of a customer previously created with the email attribute.

Card example and validation rules

curl https://api.onvopay.com/v1/payment-methods \
-X POST \
-H "Authorization: Bearer $ONVO_PUBLISHABLE_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "card",
"card": {
"number": "4242424242424242",
"expMonth": 12,
"expYear": 2028,
"cvv": "123",
"holderName": "Maria Rodriguez"
},
"billing": {
"name": "Maria Rodriguez",
"address": {
"country": "CR"
}
},
"customer": {
"name": "Maria Rodriguez",
"email": "maria@example.com"
}
}'

To avoid 400 validation errors before the card is tokenized, validate these fields in your form:

  • card.holderName: non-empty text.
  • card.number: a digits-only string, without spaces or separators, and a number that passes Luhn validation.
  • card.expMonth: integer between 1 and 12.
  • card.expYear: integer between 2023 and 2100. Also validate that the card is not expired before sending it.
  • card.cvv: string with 3 or 4 digits when you send it.

If the payload does not meet these rules, ONVO responds with 400 and a validation error. For example, an invalid card number can return:

{
"statusCode": 400,
"message": ["card.number Card number is invalid"],
"error": "Bad Request"
}

When you create a card payment method, ONVO tokenizes and verifies the card before creating the object. If tokenization fails because the card details are invalid, the endpoint responds with 400 and no payment method is created.

{
"statusCode": 400,
"type": "OnvoAPIError",
"code": "cards.invalid_card_info",
"message": "There was an error with the card information provided. Please review card number, expiration date and cvv",
"path": "/v1/payment-methods",
"details": {
"card": {
"reason": "issuer_declined",
"declineCode": "55",
"declineMessage": "Incorrect PIN"
}
},
"timestamp": "2026-04-29T17:36:28.477Z"
}

Structured decline details

details.card is optional and only appears when ONVO has safe information about a handled card-verification decline. Use reason to choose the general treatment:

ReasonMeaning
issuer_declinedThe issuer declined the verification.
gateway_declinedThe gateway or acquirer explicitly reported a non-technical acceptance, authentication, merchant, or risk rule.
onvo_declinedONVO applied an eligibility or security rule.
processor_errorA technical failure occurred while processing the verification.
unknownThere was not enough information to attribute the decline.

declineCode can come from the gateway or issuer, or be a stable ONVO fallback; it is not a universal code. declineMessage is informational: do not parse it for business logic or depend on it for localization.

When no specific decline information is available, ONVO returns the default values: unknown in reason, generic_decline in declineCode, and a generic message in declineMessage.

Show useful guidance to the buyer based on reason. Do not promise that a retry will resolve the decline; for issuer_declined, you can suggest checking the card details, contacting the issuer, or using another payment method.

Create an intent

curl https://api.onvopay.com/v1/payment-intents \
-X POST \
-H "Authorization: Bearer $ONVO_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 500000,
"currency": "CRC",
"description": "Order #1001"
}'

Confirm the intent

Use the intent id and the id from the payment method created earlier.

curl https://api.onvopay.com/v1/payment-intents/$PAYMENT_INTENT_ID/confirm \
-X POST \
-H "Authorization: Bearer $ONVO_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"paymentMethodId": "cl502zv0d0127ebdp3zt27651"
}'

Common states

StateMeaning
requires_payment_methodInitial state. The intent also stays in this state if confirmation fails, for example because a card is declined.
requires_actionThe payment method needs an additional buyer action, such as 3DS authentication.
processingONVO is processing the payment.
succeededThe payment succeeded.
canceledThe intent was canceled.

Best practices

  • Store the intent id with your payment.
  • Use metadata for internal cart, payment, or customer IDs.
  • Confirm the final state by webhook before delivering digital goods or marking the payment as complete.