Skip to main content
Extend the validity of an existing authorization when fulfillment takes longer than the initial authorization period. In the United States, authorizations are valid for 3 days, and up to 29 days in other regions. Reauthorization allows the merchant to keep funds available without requiring the buyer to re-approve payment. Common scenarios include:
  • Custom or made-to-order items with extended production times
  • Backorders with delayed inventory availability
  • International shipments with longer fulfillment windows
  • Pre-orders for unreleased products
  • Fulfillment delays due to operational or external factors
  • High-value orders requiring additional verification before shipping
This integration uses the Payments API v2 to reauthorize an existing authorization and generate a new authorization with a refreshed expiration date.

Prerequisites

  • Complete the quick start PayPal integration.
  • An existing authorization ID that is past the initial honor period (3+ days old).
  • A server environment capable of securely calling PayPal REST APIs.

Integrate server side

Add the following endpoint to your existing server file from the quick start integration.
# Reauthorize for the same amount
curl -X POST https://api-m.sandbox.paypal.com/v2/payments/authorizations/AUTHORIZATION_ID/reauthorize \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -d '{}'

# Reauthorize for a higher amount (up to 115% of original)
curl -X POST https://api-m.sandbox.paypal.com/v2/payments/authorizations/AUTHORIZATION_ID/reauthorize \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -d '{
    "amount": {
      "currency_code": "USD",
      "value": "115.00"
    }
  }'

# Get reauthorization status
curl -X GET https://api-m.sandbox.paypal.com/v2/payments/authorizations/NEW_AUTHORIZATION_ID \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ACCESS_TOKEN"
// Add endpoint for reauthorization
app.post('/api/paypal/reauthorize-payment', async (req, res) => {
  const { authorizationID, amount } = req.body;
  if (!authorizationID) {
    return res.status(400).json({ error: 'Missing authorizationID' });
  }
  const accessToken = await getPayPalAccessToken();
  const apiBase = process.env.PAYPAL_API_BASE || 'https://api-m.sandbox.paypal.com';
  const reauthorizeRequest = {
    amount: amount || undefined // Optional: up to 115% of original amount
  };
  try {
    const response = await fetch(
      `${apiBase}/v2/payments/authorizations/${authorizationID}/reauthorize`,
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${accessToken}`,
        },
        body: JSON.stringify(reauthorizeRequest),
      }
    );
    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.message || 'Reauthorization failed');
    }
    const reauthorization = await response.json();
    res.json({
      status: 'REAUTHORIZED',
      newAuthorizationId: reauthorization.id,
      amount: reauthorization.amount,
      expirationTime: reauthorization.expiration_time
    });
  } catch (err) {
    if (err.message.includes('AUTHORIZATION_ALREADY_COMPLETED')) {
      res.status(400).json({ error: 'Authorization already captured or voided' });
    } else if (err.message.includes('REAUTHORIZE_NOT_ALLOWED')) {
      res.status(400).json({ error: 'Reauthorization not allowed (too soon or expired)' });
    } else {
      res.status(500).json({ error: err.message });
    }
  }
});
from paypalrestsdk import Api, Authorization
import os

# Configure PayPal SDK
api = Api({
  'mode': 'sandbox',
  'client_id': os.environ['PAYPAL_CLIENT_ID'],
  'client_secret': os.environ['PAYPAL_CLIENT_SECRET']
})

# Process reauthorization
@app.route('/api/paypal/reauthorize-payment', methods=['POST'])
def reauthorize_payment():
  try:
    authorization_id = request.json.get('authorizationID')
    amount = request.json.get('amount')

    if not authorization_id:
      return jsonify({'error': 'Missing authorizationID'}), 400

    # Get authorization
    authorization = Authorization.find(authorization_id)

    # Reauthorize with optional amount
    reauthorize_data = {}
    if amount:
      reauthorize_data['amount'] = {
        'value': amount['value'],
        'currency_code': amount.get('currency_code', 'USD')
      }

    # Execute reauthorization
    reauthorization = authorization.reauthorize(reauthorize_data)

    if reauthorization.success():
      return jsonify({
        'status': 'REAUTHORIZED',
        'newAuthorizationId': reauthorization.id,
        'amount': reauthorization.amount,
        'expirationTime': reauthorization.expiration_time
      })
    else:
      error = reauthorization.error
      if error.get('name') == 'AUTHORIZATION_ALREADY_COMPLETED':
        return jsonify({
          'error': 'Authorization already captured or voided'
        }), 400
      elif error.get('name') == 'REAUTHORIZE_NOT_ALLOWED':
        return jsonify({
          'error': 'Reauthorization not allowed (too soon or expired)'
        }), 400
      else:
        return jsonify({'error': error.get('message')}), 500

  except Exception as e:
    return jsonify({'error': str(e)}), 500
import com.paypal.core.PayPalEnvironment;
import com.paypal.core.PayPalHttpClient;
import com.paypal.payments.AuthorizationsReauthorizeRequest;
import com.paypal.payments.Authorization;
import com.paypal.payments.Money;

HttpClient httpClient = new PayPalHttpClient(
  new SandboxEnvironment(
    System.getenv("PAYPAL_CLIENT_ID"),
    System.getenv("PAYPAL_CLIENT_SECRET")
  )
);

// Process reauthorization
@PostMapping("/api/paypal/reauthorize-payment")
public ResponseEntity<?> reauthorizePayment(
  @RequestBody ReauthorizeRequest reauthorizeRequest
) {
  try {
    String authorizationId = reauthorizeRequest.getAuthorizationID();

    if (authorizationId == null || authorizationId.isEmpty()) {
      return ResponseEntity.badRequest().body(
        Map.of("error", "Missing authorizationID")
      );
    }

    AuthorizationsReauthorizeRequest request =
      new AuthorizationsReauthorizeRequest(authorizationId);

    // Set reauthorization amount if provided (up to 115% of original)
    if (reauthorizeRequest.getAmount() != null) {
      ReauthorizeRequestBody body = new ReauthorizeRequestBody()
        .amount(new Money()
          .value(reauthorizeRequest.getAmount().getValue())
          .currencyCode(reauthorizeRequest.getAmount()
            .getCurrencyCode() != null ?
            reauthorizeRequest.getAmount().getCurrencyCode() : "USD")
        );
      request.requestBody(body);
    }

    HttpResponse<Authorization> response = httpClient.execute(request);
    Authorization reauthorization = response.result();

    return ResponseEntity.ok(Map.of(
      "status", "REAUTHORIZED",
      "newAuthorizationId", reauthorization.id(),
      "amount", reauthorization.amount(),
      "expirationTime", reauthorization.expirationTime()
    ));

  } catch (HttpClientException e) {
    String issue = extractIssue(e);
    if ("AUTHORIZATION_ALREADY_COMPLETED".equals(issue)) {
      return ResponseEntity.badRequest().body(
        Map.of("error", "Authorization already captured or voided")
      );
    } else if ("REAUTHORIZE_NOT_ALLOWED".equals(issue)) {
      return ResponseEntity.badRequest().body(
        Map.of("error", "Reauthorization not allowed (too soon or expired)")
      );
    }
    return ResponseEntity.status(500).body(
      Map.of("error", e.getMessage())
    );
  }
}
<?php
use PayPalCheckoutSdk\Core\SandboxEnvironment;
use PayPalCheckoutSdk\Core\PayPalHttpClient;
use PayPalCheckoutSdk\Payments\AuthorizationsReauthorizeRequest;

$environment = new SandboxEnvironment(
  getenv('PAYPAL_CLIENT_ID'),
  getenv('PAYPAL_CLIENT_SECRET')
);
$client = new PayPalHttpClient($environment);

// Process reauthorization
$app->post('/api/paypal/reauthorize-payment', function ($request, $response) use ($client) {
  $body = $request->getParsedBody();
  $authorizationID = $body['authorizationID'] ?? null;

  if (!$authorizationID) {
    $response->getBody()->write(json_encode([
      'error' => 'Missing authorizationID'
    ]));
    return $response->withStatus(400)->withHeader('Content-Type', 'application/json');
  }

  $reauthorizeRequest = new AuthorizationsReauthorizeRequest($authorizationID);

  // Set reauthorization amount if provided (up to 115% of original)
  if (isset($body['amount'])) {
    $reauthorizeRequest->body = [
      'amount' => [
        'value' => $body['amount']['value'],
        'currency_code' => $body['amount']['currency_code'] ?? 'USD'
      ]
    ];
  }

  try {
    $reauthorizeResponse = $client->execute($reauthorizeRequest);
    $reauthorization = $reauthorizeResponse->result;

    $response->getBody()->write(json_encode([
      'status' => 'REAUTHORIZED',
      'newAuthorizationId' => $reauthorization->id,
      'amount' => $reauthorization->amount,
      'expirationTime' => $reauthorization->expiration_time
    ]));
    return $response->withHeader('Content-Type', 'application/json');

  } catch (HttpException $e) {
    $statusCode = $e->statusCode;
    $errorData = json_decode($e->getMessage(), true);
    $issue = $errorData['details'][0]['issue'] ?? '';

    if ($issue === 'AUTHORIZATION_ALREADY_COMPLETED') {
      $response->getBody()->write(json_encode([
        'error' => 'Authorization already captured or voided'
      ]));
      return $response->withStatus(400)->withHeader('Content-Type', 'application/json');
    } else if ($issue === 'REAUTHORIZE_NOT_ALLOWED') {
      $response->getBody()->write(json_encode([
        'error' => 'Reauthorization not allowed (too soon or expired)'
      ]));
      return $response->withStatus(400)->withHeader('Content-Type', 'application/json');
    }

    $response->getBody()->write(json_encode([
      'error' => $e->getMessage()
    ]));
    return $response->withStatus(500)->withHeader('Content-Type', 'application/json');
  }
});
require 'paypal-sdk'

# Configure PayPal SDK
PayPal::SDK.configure(
  :mode => "sandbox",
  :app_id => ENV['PAYPAL_APP_ID'],
  :client_id => ENV['PAYPAL_CLIENT_ID'],
  :client_secret => ENV['PAYPAL_CLIENT_SECRET']
)

# Process reauthorization
post '/api/paypal/reauthorize-payment' do
  request_body = JSON.parse(request.body.read)
  authorization_id = request_body['authorizationID']

  if authorization_id.nil? || authorization_id.empty?
    status 400
    return { error: 'Missing authorizationID' }.to_json
  end

  begin
    # Get authorization
    authorization = PayPal::SDK::PaymentsApi::Authorization.find(authorization_id)

    # Prepare reauthorization data
    reauthorize_data = {}
    if request_body['amount']
      reauthorize_data[:amount] = {
        value: request_body['amount']['value'],
        currency_code: request_body['amount']['currency_code'] || 'USD'
      }
    end

    # Execute reauthorization
    reauthorization = authorization.reauthorize(reauthorize_data)

    if reauthorization.success?
      content_type :json
      {
        status: 'REAUTHORIZED',
        newAuthorizationId: reauthorization.id,
        amount: reauthorization.amount,
        expirationTime: reauthorization.expiration_time
      }.to_json
    else
      error = reauthorization.error
      if error['name'] == 'AUTHORIZATION_ALREADY_COMPLETED'
        status 400
        { error: 'Authorization already captured or voided' }.to_json
      elsif error['name'] == 'REAUTHORIZE_NOT_ALLOWED'
        status 400
        { error: 'Reauthorization not allowed (too soon or expired)' }.to_json
      else
        status 500
        { error: error['message'] }.to_json
      end
    end

  rescue => e
    status 500
    { error: e.message }.to_json
  end
end

Test endpoint

# Reauthorize for the same amount
curl -X POST http://localhost:3000/api/paypal/reauthorize-payment \
  -H "Content-Type: application/json" \
  -d '{"authorizationID": "5AB67890CD123456E"}'

# Expected success response:
# {"status":"REAUTHORIZED","newAuthorizationId":"8CD12345EF678901A","amount":{"currency_code":"USD","value":"100.00"},"expirationTime":"2024-02-05T10:23:23Z"}

# Reauthorize for a higher amount (up to 115% of original)
curl -X POST http://localhost:3000/api/paypal/reauthorize-payment \
  -H "Content-Type: application/json" \
  -d '{
    "authorizationID": "5AB67890CD123456E",
    "amount": {
      "currency_code": "USD",
      "value": "115.00"
    }
  }'

# Expected success response:
# {"status":"REAUTHORIZED","newAuthorizationId":"8CD12345EF678901A","amount":{"currency_code":"USD","value":"115.00"},"expirationTime":"2024-02-05T10:23:23Z"}
// Test reauthorize for the same amount
const sameAmountResponse = await fetch('http://localhost:3000/api/paypal/reauthorize-payment', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    authorizationID: '5AB67890CD123456E'
  })
});
const sameAmount = await sameAmountResponse.json();
console.log('Reauthorized:', sameAmount);
// Expected: {"status":"REAUTHORIZED","newAuthorizationId":"8CD12345EF678901A","amount":{"currency_code":"USD","value":"100.00"},"expirationTime":"2024-02-05T10:23:23Z"}

// Test reauthorize for a higher amount (up to 115% of original)
const higherAmountResponse = await fetch('http://localhost:3000/api/paypal/reauthorize-payment', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    authorizationID: '5AB67890CD123456E',
    amount: {
      currency_code: 'USD',
      value: '115.00'
    }
  })
});
const higherAmount = await higherAmountResponse.json();
console.log('Reauthorized with higher amount:', higherAmount);
// Expected: {"status":"REAUTHORIZED","newAuthorizationId":"8CD12345EF678901A","amount":{"currency_code":"USD","value":"115.00"},"expirationTime":"2024-02-05T10:23:23Z"}
import requests

# Test reauthorize for the same amount
same_amount_response = requests.post(
  'http://localhost:3000/api/paypal/reauthorize-payment',
  headers={'Content-Type': 'application/json'},
  json={'authorizationID': '5AB67890CD123456E'}
)
print('Reauthorized:', same_amount_response.json())
# Expected: {"status":"REAUTHORIZED","newAuthorizationId":"8CD12345EF678901A","amount":{"currency_code":"USD","value":"100.00"},"expirationTime":"2024-02-05T10:23:23Z"}

# Test reauthorize for a higher amount (up to 115% of original)
higher_amount_response = requests.post(
  'http://localhost:3000/api/paypal/reauthorize-payment',
  headers={'Content-Type': 'application/json'},
  json={
    'authorizationID': '5AB67890CD123456E',
    'amount': {
      'currency_code': 'USD',
      'value': '115.00'
    }
  }
)
print('Reauthorized with higher amount:', higher_amount_response.json())
# Expected: {"status":"REAUTHORIZED","newAuthorizationId":"8CD12345EF678901A","amount":{"currency_code":"USD","value":"115.00"},"expirationTime":"2024-02-05T10:23:23Z"}
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;

HttpClient client = HttpClient.newHttpClient();

// Test reauthorize for the same amount
String sameAmountBody = "{\"authorizationID\":\"5AB67890CD123456E\"}";
HttpRequest sameAmountRequest = HttpRequest.newBuilder()
  .uri(URI.create("http://localhost:3000/api/paypal/reauthorize-payment"))
  .header("Content-Type", "application/json")
  .POST(HttpRequest.BodyPublishers.ofString(sameAmountBody))
  .build();

HttpResponse<String> sameAmountResponse = client.send(
  sameAmountRequest,
  HttpResponse.BodyHandlers.ofString()
);
System.out.println("Reauthorized: " + sameAmountResponse.body());
// Expected: {"status":"REAUTHORIZED","newAuthorizationId":"8CD12345EF678901A","amount":{"currency_code":"USD","value":"100.00"},"expirationTime":"2024-02-05T10:23:23Z"}

// Test reauthorize for a higher amount (up to 115% of original)
String higherAmountBody = "{\"authorizationID\":\"5AB67890CD123456E\",\"amount\":{\"currency_code\":\"USD\",\"value\":\"115.00\"}}";
HttpRequest higherAmountRequest = HttpRequest.newBuilder()
  .uri(URI.create("http://localhost:3000/api/paypal/reauthorize-payment"))
  .header("Content-Type", "application/json")
  .POST(HttpRequest.BodyPublishers.ofString(higherAmountBody))
  .build();

HttpResponse<String> higherAmountResponse = client.send(
  higherAmountRequest,
  HttpResponse.BodyHandlers.ofString()
);
System.out.println("Reauthorized with higher amount: " + higherAmountResponse.body());
// Expected: {"status":"REAUTHORIZED","newAuthorizationId":"8CD12345EF678901A","amount":{"currency_code":"USD","value":"115.00"},"expirationTime":"2024-02-05T10:23:23Z"}
<?php
// Test reauthorize for the same amount
$sameAmountData = json_encode([
  'authorizationID' => '5AB67890CD123456E'
]);

$sameAmountResponse = file_get_contents(
  'http://localhost:3000/api/paypal/reauthorize-payment',
  false,
  stream_context_create([
    'http' => [
      'method' => 'POST',
      'header' => 'Content-Type: application/json',
      'content' => $sameAmountData
    ]
  ])
);
echo "Reauthorized: " . $sameAmountResponse . "\n";
// Expected: {"status":"REAUTHORIZED","newAuthorizationId":"8CD12345EF678901A","amount":{"currency_code":"USD","value":"100.00"},"expirationTime":"2024-02-05T10:23:23Z"}

// Test reauthorize for a higher amount (up to 115% of original)
$higherAmountData = json_encode([
  'authorizationID' => '5AB67890CD123456E',
  'amount' => [
    'currency_code' => 'USD',
    'value' => '115.00'
  ]
]);

$higherAmountResponse = file_get_contents(
  'http://localhost:3000/api/paypal/reauthorize-payment',
  false,
  stream_context_create([
    'http' => [
      'method' => 'POST',
      'header' => 'Content-Type: application/json',
      'content' => $higherAmountData
    ]
  ])
);
echo "Reauthorized with higher amount: " . $higherAmountResponse . "\n";
// Expected: {"status":"REAUTHORIZED","newAuthorizationId":"8CD12345EF678901A","amount":{"currency_code":"USD","value":"115.00"},"expirationTime":"2024-02-05T10:23:23Z"}
require 'net/http'
require 'json'

# Test reauthorize for the same amount
uri = URI('http://localhost:3000/api/paypal/reauthorize-payment')
same_amount_request = Net::HTTP::Post.new(uri)
same_amount_request['Content-Type'] = 'application/json'
same_amount_request.body = {
  authorizationID: '5AB67890CD123456E'
}.to_json

same_amount_response = Net::HTTP.start(uri.hostname, uri.port) do |http|
  http.request(same_amount_request)
end
puts "Reauthorized: #{same_amount_response.body}"
# Expected: {"status":"REAUTHORIZED","newAuthorizationId":"8CD12345EF678901A","amount":{"currency_code":"USD","value":"100.00"},"expirationTime":"2024-02-05T10:23:23Z"}

# Test reauthorize for a higher amount (up to 115% of original)
higher_amount_request = Net::HTTP::Post.new(uri)
higher_amount_request['Content-Type'] = 'application/json'
higher_amount_request.body = {
  authorizationID: '5AB67890CD123456E',
  amount: {
    currency_code: 'USD',
    value: '115.00'
  }
}.to_json

higher_amount_response = Net::HTTP.start(uri.hostname, uri.port) do |http|
  http.request(higher_amount_request)
end
puts "Reauthorized with higher amount: #{higher_amount_response.body}"
# Expected: {"status":"REAUTHORIZED","newAuthorizationId":"8CD12345EF678901A","amount":{"currency_code":"USD","value":"115.00"},"expirationTime":"2024-02-05T10:23:23Z"}

Best practices

  • Reauthorize within the valid window: Reauthorization is allowed only between days 4-29 after the original authorization, depending on region.
  • Reauthorize only once: Each authorization can be reauthorized a single time. If more time is required, create a new order.
  • Monitor authorization expiration: Track expiration dates and trigger reauthorization before the authorization lapses.
  • Respect amount limits: You can reauthorize for up to 115% of the original amount in most regions. Regional limits may vary.
  • Handle failures gracefully: If reauthorization fails, void the original authorization and request a new payment from the customer.

Important details

  • Authorization validity periods: Authorizations are valid for 3 days in the US and up to 29 days in most other regions. After expiration, funds are automatically released to the customer.
  • Reauthorization timing window: You can only reauthorize between days 4-29 after the initial authorization. Attempts before day 3 or after day 29 will fail.
  • Single reauthorization limit: Each authorization can be reauthorized only once. If you need more time after reauthorization, void the authorization and create a new order.
  • No customer interaction required: Reauthorization happens server-side without customer approval. However, consider notifying customers as a courtesy when extending payment holds.
  • Handling reauthorization failures: If reauthorization fails, void the original authorization and request new payment from the customer. Common failures include insufficient funds, authorization already captured or voided, or attempting outside the valid window.

Test your integration

Run the following standard tests on your integration.
Test scenarioSetupExpected result
Successful reauthorizationCreate authorization, wait 4+ days, reauthorize for same amountNew authorization created with a fresh expiration date.
Reauthorize for higher amountReauthorize for 110% of original amountNew authorization created for increased amount.
Reauthorize too soonAttempt within 3 days of original authorizationError: reauthorization not available yet.
Reauthorize expired authorizationAttempt after 30+ daysError: authorization expired and cannot be reauthorized.
Reauthorize more than onceReauthorize successfully, then attempt againError: cannot reauthorize more than once.

Go-live checklist

  • Test reauthorization in sandbox using the time machine.
  • Verify handling of amount limits (up to 115%).
  • Test error handling for all reauthorization error cases.
  • Implement authorization expiration monitoring.
  • Switch to production API credentials.