Hold then capture
How to authorize a card hold and capture only when you're ready to charge.
Overview
Hold Then Capture splits a card transaction into two steps: authorization (reserving the funds) and capture (actually charging). You place a hold when the customer checks out, then capture once you're ready to fulfill — up to 7 days later. If you cancel instead, no charge is made.
Available for Visa and Mastercard only, and currently enabled only for merchants acquired through Robinsons Bank (RBank). Account activation is required — contact [email protected] to enable it. Shopify merchants with the Credit/Debit Card via PayMongo plugin can enable it directly from the Shopify admin.
Hold a payment
Create a Payment Intent with capture_type set to "manual". The rest of the flow — creating a Payment Method and attaching it — is the same as a standard card payment.
const intent = await fetch('https://api.paymongo.com/v1/payment_intents', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa('sk_test_YOUR_SECRET_KEY:')
},
body: JSON.stringify({
data: {
attributes: {
amount: 50000,
currency: 'PHP',
payment_method_allowed: ['card'],
capture_type: 'manual',
description: 'Order #9012'
}
}
})
}).then(r => r.json());After the customer completes 3D Secure authentication, the Payment Intent moves to awaiting_capture. Funds are reserved on the customer's card but not yet charged.
The hold expires automatically after 7 days. Expired holds release the reserved funds back to the customer and cannot be captured — you must restart the payment flow if this happens.
Capture a payment
When you're ready to charge, call the capture endpoint. You can capture the full amount or a partial amount — set amount in the request body to capture less than the Payment Intent amount. If no amount is specified, the full amount is captured.
await fetch(`https://api.paymongo.com/v1/payment_intents/${intentId}/capture`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + btoa('sk_test_YOUR_SECRET_KEY:')
},
body: JSON.stringify({
data: {
attributes: {
amount: 50000 // omit to capture full amount
}
}
})
}).then(r => r.json());To void the hold instead of charging, call the cancel endpoint:
await fetch(`https://api.paymongo.com/v1/payment_intents/${intentId}/cancel`, {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('sk_test_YOUR_SECRET_KEY:')
}
}).then(r => r.json());The PayMongo dashboard shows held payments under three tabs: Hold, Captured, and Returned. Holds expiring within 2 days are flagged as Expiring Soon.
Updated 19 days ago
