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. 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.