Radar
Reviews
Uncaptured payments

Reviewing uncaptured payments

If your Stripe integration uses auth and capture, read on to make sure reviews work well for you.

By default, with Stripe you create payments in one step, and with no further action on your part the funds will be sent to your bank account.

However, Stripe also supports two-step payments, often called auth and capture. If your integration uses this technique, it’s important to understand that approving a review and capturing a payment are separate actions.

Reviewing uncaptured payments in the Dashboard

When an uncaptured payment is placed in review, the Dashboard will display both a capture button and a set of buttons to close the review by approving or refunding. (For uncaptured payments, refunding is often called “releasing”.)

Important: approving the review will not automatically capture the charge. You will still need to click the capture button.

Using the API to automatically capture approved payments

Through the API, you can set up your integration to:

  • Immediately capture payments not placed in review
  • Leave payments placed in review uncaptured
  • Once the review is approved, capture the payment

Immediately capture payments not placed in review

To create an uncaptured payment, set the capture behavior accordingly in the API request. On success, check the payment intent’s Review attribute. If the attribute is empty, capture the charge.

# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' # Get the credit card details submitted by the form # Create a PaymentIntent with manual capture payment_intent = Stripe::PaymentIntent.create({ amount: 1000, currency: 'usd', payment_method: '{{PAYMENT_METHOD_ID}}', description: 'Example charge', confirm: true, capture_method: 'manual', }) # Check if the payment is in review. If not, capture it. if !payment_intent.review payment_intent.capture end
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' # Get the credit card details submitted by the form # Create a PaymentIntent with manual capture payment_intent = stripe.PaymentIntent.create( amount=2000, currency='usd', description='Example charge', confirm=True, capture_method='manual', ) # Check if the payment is in review. If not, capture it. if not payment_intent.review: payment_intent.capture
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); // Get the credit card details submitted by the form // Create a PaymentIntent with manual capture $payment_intent = \Stripe\PaymentIntent::create([ 'amount' => 1000, 'currency' => 'usd', 'payment_method' => '{{PAYMENT_METHOD_ID}}', 'description' => 'Example charge', 'confirm' => true, 'capture_method' => 'manual', ]); // Check if the payment is in review. If not, capture it. if(!$payment_intent->review) { $payment_intent->capture(); }
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; // Get the credit card details submitted by the form // Create a PaymentIntent with manual capture PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1000L) .setCurrency("usd") .setPaymentMethod("{{PAYMENT_METHOD_ID}}") .setDescription("Example charge") .setConfirm(true) .setCaptureMethod(PaymentIntentCreateParams.CaptureMethod.MANUAL) .build(); PaymentIntent paymentIntent = PaymentIntent.create(params); // Check if the payment is in review. If not, capture it. if(!paymentIntent.getReview()) { paymentIntent.capture(); }
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); // Get the credit card details submitted by the form // Create a PaymentIntent with manual capture var paymentIntent = await stripe.paymentIntents.create({ amount: 1000, currency: 'usd', payment_method: '{{PAYMENT_METHOD_ID}}', description: 'Example charge', confirm: true, capture_method: 'manual', }); // Check if the payment is in review. If not, capture it. if(!payment_intent.review) { var paymentIntentCaptured = await stripe.paymentIntents.capture(payment_intent.id); }
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" // Get the credit card details submitted by the form // Create a PaymentIntent with manual capture params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1000), Currency: stripe.String(string(stripe.CurrencyUSD)), PaymentMethod: stripe.String("{{PAYMENT_METHOD_ID}}"), Description: stripe.String("Example charge"), Confirm: stripe.Bool(true), CaptureMethod: stripe.String(string(stripe.PaymentIntentCaptureMethodManual)), } pi, _ := paymentintent.New(params) // Check if the payment is in review. If not, capture it. if pi.Review == nil { paymentintent.Capture(pi.ID, nil) }
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; // Get the credit card details submitted by the form // Create a PaymentIntent with manual capture var options = new PaymentIntentCreateOptions { Amount = 1000, Currency = "usd", Description = "Example charge", Confirm = true, CaptureMethod = "manual", }; var service = new PaymentIntentService(); var paymentIntent = service.Create(options); // Check if the payment is in review. If not, capture it. if(paymentIntent.Review == null) { service.Capture(paymentIntent.Id, null); }

Capturing a payment after a review is approved

By design, the prior step left payments in review uncaptured. In this step you’ll use webhooks to automate the process of capturing these payments upon approval.

Start by configuring your webhooks to listen for the review.closed event. The event data includes the Review object, and the object’s reason attribute will indicate whether the review was approved, or if it was closed for some other reason (e.g., the payment was refunded).

// Review object included in review.closed event webhook. { "id": "prv_08voh1589O8KAxCGPcIQpmkz", "object": "review", "payment_intent": "pi_1D0CsEITpIrAk4QYdrWDnbRS", "created": 1474379631, "livemode": false, "open": false, "reason": "approved" }

If reason is 'approved', you’ll want to capture the charge.

# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' post "/my/webhook/url" do event_json = JSON.parse(request.body.read) event = Stripe::Event.retrieve(event_json["id"]) if event.type == 'review.closed' review = event.object if review.reason == 'approved' pi = Stripe::PaymentIntent.retrieve(review.payment_intent) pi.capture end end status 200 end

To capture approved payments, the review process must be completed within seven days. Otherwise, as with any other uncaptured payment, the authorization will automatically expire and the payment can no longer be captured.

Was this page helpful?
Questions? Contact us.
Developer tutorials on YouTube.