Crypto payments via API
With Crypto, you create and confirm a Payment Intent from your server, then redirect the buyer to an ONVO-hosted form. The buyer selects an available asset and network and sends payment from their wallet. ONVO updates the intent asynchronously, even if the buyer closes the page.
Before you start
- Crypto via API must be enabled for your account and the mode you plan to use. Contact ONVO to enable it; availability in Checkout does not mean it is enabled for API payments.
- It is available for Costa Rican accounts and Payment Intents in
USDorCRC. - Amounts use the currency's smallest denomination, not crypto units:
1000inUSDmeans USD 10.00. - Use your Secret API Key on your server and configure webhooks.
- This flow supports one-time payments with automatic capture. Manual capture, subscriptions, and refunds through the Refunds API are not supported.
1. Create the Payment Intent
Create one intent per order and save its id on your server as PAYMENT_INTENT_ID.
curl https://api.onvopay.com/v1/payment-intents \
-X POST \
-H "Authorization: Bearer $ONVO_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 1000,
"currency": "USD",
"description": "Order #1001"
}'
2. Create the Payment Method
Send type: "crypto" and a crypto object containing fullName and email. The full name must be a non-empty string and the email must be valid; ONVO trims leading and trailing whitespace from both values.
Send the buyer's real full name and a valid email address they can access. This email is used for communications about their Crypto payment, including notices about any applicable automatic refund. Using fictitious details, the merchant's email, or an address the buyer cannot access may prevent them from receiving important payment information.
The example details are illustrative: replace them with the buyer's real details when processing a real payment.
curl https://api.onvopay.com/v1/payment-methods \
-X POST \
-H "Authorization: Bearer $ONVO_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "crypto",
"crypto": {
"fullName": "María Rodríguez",
"email": "maria@example.com"
}
}'
Save the returned id to confirm the intent. You may associate a customer using customerId or customer, but that does not replace the required crypto fields. These details populate the buyer information in the form; the buyer may still need to accept terms or complete verification.
Do not send cryptoPaymentId or the detail objects for other payment method types. The payment session starts when you confirm the intent. Each Crypto method is bound to one intent and cannot be reused for another order.
3. Confirm and redirect
Replace PAYMENT_METHOD_ID with the created method's id. For Crypto, returnUrl is required and must be an absolute https:// or http:// URL for your application.
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": "PAYMENT_METHOD_ID",
"returnUrl": "https://shop.example.com/orders/1001/return"
}'
A new session returns requires_action and nextAction. This response is abbreviated and uses illustrative identifiers:
{
"id": "PAYMENT_INTENT_ID",
"status": "requires_action",
"amount": 1000,
"receivedAmount": 0,
"currency": "USD",
"nextAction": {
"type": "redirect_to_url",
"redirectToUrl": {
"url": "https://checkout.onvopay.com/crypto/CRYPTO_SESSION_ID",
"returnUrl": "https://shop.example.com/orders/1001/return"
}
}
}
Redirect the browser to nextAction.redirectToUrl.url exactly as returned by the API. Do not construct this URL or substitute paymentMethod.crypto.hostedUrl. The ONVO form offers English and Spanish and starts in English.
Confirmation also creates a pending charge to record the attempt. Creating that charge or receiving a URL does not mean the payment is complete.
4. Verify the result on your server
Listen for payment-intent.succeeded using the authentication mechanism described in Webhooks. You can also retrieve the intent from your server:
curl "https://api.onvopay.com/v1/payment-intents/$PAYMENT_INTENT_ID" \
-H "Authorization: Bearer $ONVO_SECRET_KEY"
Use the ID saved for your order and verify its amount, currency, and status: "succeeded" before fulfillment. Complete each order only once, even if you query the API and receive a webhook for the same payment.
| Intent status | Meaning and action |
|---|---|
requires_action | The buyer must complete the form. Use nextAction when present. |
processing | Payment is pending, for example while a transfer is being confirmed or the amount is incomplete. Do not fulfill the order. |
succeeded | ONVO confirmed the complete payment. You can fulfill the order. |
requires_payment_method | The attempt failed or was canceled before funds were received. Check lastPaymentError and allow another attempt. |
canceled | The merchant canceled the intent. Do not reuse it to collect another payment. |
Partial and pending payments
If the buyer sends less than the required amount, the intent may remain in processing. While the session still accepts payment, the form allows the buyer to send the remaining amount. Do not close the flow or mark the order as paid just because a transfer occurred.
receivedAmount is the amount ONVO has recorded for the intent, in the same units as amount; it is not necessarily the amount the wallet just sent. A session in hold is still awaiting confirmation and may have receivedAmount: 0.
The session status, a thank-you message in the form, or nextAction: null does not replace the intent status. Even if the form is unavailable or expired, processing is still not a successful payment. Retrieve the status and wait for server confirmation; do not depend on a payment-intent.processing event.
Returning to the merchant
When ONVO confirms succeeded, the form displays the result and automatically redirects to returnUrl after a few seconds. The return adds these parameters:
| Parameter | Description |
|---|---|
payment_intent_id | The intent ID. |
status | The intent status observed when returning. |
outcome | success or cancel, according to the return flow's outcome. |
These parameters are informational and can be modified in the browser. Returning with outcome=success is not proof of payment. Retrieve the intent from your server or process the authenticated webhook. Payment updates do not depend on the buyer returning to your site.
Cancellation, expiration, and retries
- If the buyer closes the form with the X before sending funds, ONVO fails the charge with
crypto_payment_canceled, records the error inlastPaymentError, and returns the intent torequires_payment_method. The return hasoutcome=canceland ONVO emitspayment-intent.failed. - If funds have already arrived or a transfer is pending, closing the form does not mean payment is canceled and does not reverse the transfer. Continue checking the intent and handling its webhooks.
- To cancel the intent from your server, use Cancel a Payment Intent. This operation sets it to
canceledand emitspayment-intent.canceled; it is not a refund. - Each session has an expiration time, available in
paymentMethod.crypto.expiryDate. An expired session cannot be reused. For another attempt without pending funds, create a new Crypto method and confirm an intent that permits another attempt. - Reconfirming the same intent with its method and a valid session lets you recover the existing action. Do not create another order or intent just because the buyer returns from the form.
Common errors
The API may return 400 with a validation error (statusCode, message, error) if crypto is missing, the name is blank, or the email is invalid. These errors do not guarantee a code field. For business errors, use the response's code:
| Code | What to check |
|---|---|
payment_methods.invalid_payment_method_payload | The payload mixes details for different method types or does not meet the method's contract. |
payment_methods.crypto_not_enabled | The account, country, or mode does not have Crypto via API enabled. |
payment_methods.crypto_currency_not_supported | The intent currency must be USD or CRC. |
payment_intents.return_url_required | Confirmation is missing returnUrl. A malformed URL may also produce a validation error. |
payment_methods.crypto_already_used | The method is bound to another intent or cannot be reused. |
payment_methods.crypto_session_expired | A new attempt needs a new session. |
payment_intents.crypto_session_unavailable | The form could not be started. Retrieve the intent before retrying. |
payment_intents.crypto_settlement_in_progress | A payment update is in progress. Wait and retrieve the status before retrying cancellation. |
Testing
Use an onvo_test_ key with Crypto via API enabled in test mode. Follow the form's instructions for the test asset and network it displays; for example, if it shows testBTC, use test funds for that network. Do not send real funds to the test flow.
Validate full payment, partial payment followed by the remaining amount, pending status, closing without funds, expiration, and invalid buyer details. For every scenario, check the intent from your server and verify that your application fulfills only on succeeded, even if the buyer closes the browser.