Preauthorization & Capture
Preauthorization
Preauthorization, also known as "pre-authorization" or "funds hold," is a crucial stage in transaction processing. During this phase, the availability of funds in the cardholder's account is verified before completing the transaction. Its main objective is to reserve a certain amount of funds in the cardholder's account to ensure that there is sufficient balance available to cover the future transaction.
Preauthorization is widely used in situations where it is necessary to ensure the availability of funds before delivering a product or service. For example, in e-commerce, it can be used to ensure that the card balance is sufficient before shipping an item or making a reservation.
Implementing the Preauthorization
Prerequisites
Before starting with the preauthorization implementation, make sure you have the following:
Step 1. Generate an acceptance token.
Step 2. Tokenize the payment method (Card).
Step 3. Before creating the preauthorization, consider the following:
-
You can create a payment source so the cardholder's data is ready for future preauthorizations or payments. View process.
-
Skip the payment source creation and go directly to creating the preauthorization. View process.
Step 4. Remember that creating the preauthorization requires using your private key, which must be sent as a Bearer token:
- If you want to create a preauthorization with a payment source, you need to generate a new acceptance token and make a
POST /payment_sourcesrequest with the following JSON body:
{
"type": "CARD",
"payment_source_id": 297, // Payment source ID
"customer_email": "prueba@test.com", // Cardholder's email
"financial_operation": "PREAUTHORIZATION",
"acceptance_token": "acceptance_token", // Acceptance token
"data": {
"amount_in_cents": 100, // Preauthorization amount
"currency": "USD"
}
}
- If instead you want to create a preauthorization directly, make a
POST /payment_sourcesrequest with the following JSON body:
{
"type": "CARD",
"customer_email": "prueba@test.com", // Cardholder's email
"financial_operation": "PREAUTHORIZATION",
"token": "token_card", // Tokenized card ID
"acceptance_token": "acceptance_token", // Acceptance token
"data": {
"amount_in_cents": 100, // Preauthorization amount
"currency": "USD"
}
}
You will receive the following JSON response:
{
"data": {
"id": 335,
"public_data": {
"type": "CARD",
"financial_operation": "PREAUTHORIZATION",
"amount_in_cents": 100,
"number_of_installments": 1,
"currency": "USD"
},
"token": "tok_devtest_196_8A7308f20c2d236f42252972390b4eb1",
"type": "CARD",
"status": "PROCESSING",
"customer_email": "prueba@test.com"
}
}
⚠️ IMPORTANT: The
PROCESSINGstatus means Wompi is preparing the 3DS flow. It is not a final state. You must start polling immediately. The status will change toPENDINGonly when the transaction enters the 3DS authentication flow (i.e., when Wompi has the iframe data ready for the frontend to render).
- PROCESSING → Wompi is preparing 3DS. Start polling.
- PENDING +
three_ds_auth→ There is a 3DS iframe to render (see Step 6). - AVAILABLE → Preauthorization successful, ready for capture.
- DECLINED / ERROR → Preauthorization failed.
Step 5. Perform periodic queries to verify the preauthorization status and ensure its correct execution:
To query the preauthorization, make a GET /payment_sources/:id request — don't forget to send your private key as a Bearer token.
Poll every 3 seconds. When the status changes to PENDING, the response includes the 3DS data.
Below are examples of how to implement the long polling logic for preauthorizations:
Cargando ejemplos...
You will receive the following JSON response:
{
"data": {
"id": 335,
"public_data": {
"type": "CARD",
"financial_operation": "PREAUTHORIZATION",
"amount_in_cents": 100,
"number_of_installments": 1,
"currency": "USD"
},
"token": "tok_devtest_196_8A7308f20c2d236f42252972390b4eb1",
"type": "CARD",
"status": "PENDING",
"customer_email": "jmtm141097@gmail.com",
"extra": {
"three_ds_auth": {
"current_step": "BROWSER_INFO", // Refers to the authentication type: (BROWSER_INFO - CHALLENGE)
"current_step_status": "PENDING",
"three_ds_method_data": "<iframe id="tdsMmethodTgtFrame" name="tdsMmethodTgtFrame" style="visibility: hidden; width: 1px; height: 1px;" xmlns="http://www.w3.org/1999/xhtml"><!--.--></iframe><form id="tdsMmethodForm" name="tdsMmethodForm" action="https://api-sandbox.pa.dev.wompi.dev/v1/webhook_events/acs_response?order_id=PAYMENT_SOURCE-335" method="post" target="tdsMmethodTgtFrame" xmlns="http://www.w3.org/1999/xhtml"><input type="hidden" name="operation" value="CHALLENGE_IFRAME_BIN" /><script type="text/javascript">document.getElementById("tdsMmethodForm").submit();</script></form>"
}
}
}
}
Step 6. Render the IFRAME to continue the authentication process:
In the preauthorization response, the extra key contains relevant additional information. Specifically, we will focus on the three_ds_method_data key, which includes an HTML code snippet. This is an IFRAME block that must be integrated and displayed on the page to securely carry out the card authentication process. Additionally, you will also find the current_step key, which indicates the current authentication state and may refer to the specific type of authentication required to verify the cardholder's identity.
The possible current_step values are:
| Step | Description | Target | Action |
|---|---|---|---|
BROWSER_INFO | Browser data collection | Wompi API | Hidden iframe (1x1px) |
FINGERPRINT | Cardholder device fingerprint | ACS + Wompi API | Hidden iframe (1x1px) |
CHALLENGE | Interactive issuer verification | ACS + Wompi API | Visible iframe (modal) |
Step 7. Don't forget to perform periodic queries on the preauthorization until it reaches a final state.
The code examples from Step 5 already implement the complete polling cycle, including 3DS iframe handling and waiting until the final state (AVAILABLE, DECLINED, or ERROR). You don't need to implement additional logic — the examples cover the entire lifecycle.
This is how a successful preauthorization looks, ready to be captured:
{
"data": {
"id": 335,
"public_data": {
"type": "CARD",
"financial_operation": "PREAUTHORIZATION",
"amount_in_cents": 100,
"number_of_installments": 1,
"currency": "USD"
},
"token": "tok_devtest_196_8A7308f20c2d236f42252972390b4eb1",
"type": "CARD",
"status": "AVAILABLE",
"customer_email": "jmtm141097@gmail.com",
"extra": {
"three_ds_auth": {
"current_step": "AUTHENTICATION",
"current_step_status": "COMPLETED"
}
}
}
}
Capture
Capture is the stage where the final charge or transfer of the funds reserved during preauthorization takes place. Once the transaction is completed or the agreed-upon condition is met, a capture request is sent to obtain the funds reserved during preauthorization. This results in the transfer of funds from the cardholder's account to the merchant or service provider. Capture is essential to finalize the transaction and ensure the merchant receives the corresponding payment.
Implementing Capture
Prerequisites
Before performing a capture, make sure you have the following:
-
Have a preauthorization in
AVAILABLEstate.
Step 1. Create a transaction.
To proceed with the capture process, send a POST /transactions request. In the headers, include your merchant's private key as a Bearer token. Send the following JSON as the request body:
{
"amount_in_cents": 100, // Capture amount (must not exceed the preauthorized amount)
"public_key": "pub_prod_ffhas23932", // Merchant's public key
"currency": "USD",
"customer_email": "prueba@test.com",
"reference": "TEST34502", // Unique transaction reference
"payment_source_id": 298 // Payment source ID (preauthorization)
}
Step 2. Perform periodic transaction queries to verify the status and ensure its correct execution.
This is how a successful capture looks:
{
"data": {
"id": "1196-1689602000-65841",
"created_at": "2023-07-17T13:53:24.308Z",
"amount_in_cents": 100,
"reference": "TEST34502",
"currency": "USD",
"payment_method_type": "CARD",
"payment_method": {
"type": "CARD",
"extra": {
"name": "VISA-0031",
"type": "CAPTURE", // Indicates that the transaction is a capture type
"brand": "VISA",
"last_four": "0031",
"is_three_ds": false,
"three_ds_auth": null,
"external_identifier": "QtcGZjh9nX",
"processor_response_code": "00"
}
},
"redirect_url": null,
"status": "APPROVED", // Final transaction status
"status_message": null,
"merchant": {
"name": "Pruebas SandBox",
"legal_name": "Pruebas SandBox",
"contact_name": "Pepito Perez",
"phone_number": "+50732345634",
"logo_url": null,
"legal_id_type": "RUC",
"email": "prueba@test.com",
"legal_id": "2452345-5"
},
"taxes": []
}
}
Recurring Charges (Without 3DS)
Once the first capture is successfully completed, you can make recurring charges using the same payment_source_id without repeating the 3DS process.
Why is 3DS not required for recurring charges?
When a payment source is created with financial_operation: "PREAUTHORIZATION", the card goes through the complete 3D Secure authentication process (BROWSER_INFO + CHALLENGE). Once the payment source reaches the AVAILABLE state, it is permanently authenticated.
Any subsequent transaction using the same payment_source_id does not need 3DS re-authentication, which enables:
- Automatic recurring charges (subscriptions)
- Charges for amounts different from the preauthorized one
- Multiple frictionless captures for the user
Implementing Recurring Charges
Step 1. Create a new transaction using the same payment_source_id
Send a POST /transactions request with the same payment_source_id from the preauthorization:
{
"amount_in_cents": 100,
"public_key": "pub_prod_ffhas23932",
"currency": "USD",
"customer_email": "prueba@test.com",
"reference": "RECURRING-001", // New unique reference
"payment_source_id": 298, // Same preauthorization ID
"payment_method": {
"installments": 1
},
"signature": "f6e5d4c3..." // New signature calculated with the new reference and amount
}
- The
payment_source_idis the same one used in the original capture. - A new unique reference is required for each recurring charge.
- A new integrity signature calculated with the new reference and amount is required.
- There will be no 3DS flow — the transaction will be approved without user intervention.
Step 2. Perform periodic transaction queries
The recurring charge follows the same polling pattern as the capture. The transaction will reach a final state (APPROVED, DECLINED, etc.) without going through 3DS.
This is how an approved recurring charge looks:
{
"data": {
"id": "1196-1689700000-78923",
"created_at": "2023-07-18T17:06:40.000Z",
"amount_in_cents": 100,
"reference": "RECURRING-001",
"currency": "USD",
"payment_method_type": "CARD",
"payment_method": {
"type": "CARD",
"extra": {
"name": "VISA-0057",
"type": "CAPTURE",
"brand": "VISA",
"last_four": "0057",
"is_three_ds": false,
"three_ds_auth": null,
"external_identifier": "RcTg7Hj2Xk",
"processor_response_code": "00"
},
"installments": 1
},
"status": "APPROVED",
"status_message": null
}
}
Note that is_three_ds is false and three_ds_auth is null — confirming that 3DS authentication was not required for this charge.
Complete Flow Diagram
The following diagram shows the 3 stages of the flow and how they relate:
| Stage | Action | Endpoint | 3DS |
|---|---|---|---|
| 1. Preauthorization | Create payment source | POST /payment_sources | — |
| 2. Polling + 3DS | Check status | GET /payment_sources/{id} | ✅ Required (BROWSER_INFO, FINGERPRINT and CHALLENGE) |
| 3. Capture | Charge funds | POST /transactions | ❌ Not required |
| 4. Recurring | Subsequent charge | POST /transactions | ❌ Not required |
The payment_source_id was authenticated with 3DS during preauthorization (stage 2). All subsequent transactions with that same ID skip re-authentication, allowing automatic charges without user intervention.