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.

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

Show the message to the buyer and ask them to review the card number, expiration date, and CVV before trying to create the payment method again.

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.