Skip to main content

Create, approve,
then capture.
In that order.

The PayPal Orders API v2 is a three-step flow. HATEOAS links in every response tell you exactly which URL to call next — no hardcoding required. The one mistake most integrations make: fulfilling on APPROVED instead of COMPLETED.

3

steps

HATEOAS

link navigation

0

accounts needed

PayPal's Orders API v2 replaced the older Payments API and Express Checkout. Payments now go through three explicit steps: create an order, have the customer approve it on PayPal, then capture the funds via a second API call. Many integrations skip the capture step or fulfill the order on APPROVED status — which only means the customer clicked approve, not that money moved.

The API uses HATEOAS links in every response. Rather than building endpoint URLs yourself, you follow the links returned in each response: the approve link to send the customer to PayPal, the capture link to finalize the transaction, and the self link to check order status. Hardcoded PayPal URLs are wrong by design.

Getting a PayPal business account verified takes time, and the sandbox behaves differently from production in ways that matter. This mock gives you the full Orders API v2 flow — create, HATEOAS navigation, customer approval, capture, and PAYMENT.CAPTURE.COMPLETED webhook — so you can build and verify the integration before any PayPal account exists.

The create → approve → capture flow

Every PayPal Orders API v2 integration follows the same three steps. First you create an order with intent=CAPTURE — this reserves the authorization. PayPal responds with an order ID and a set of HATEOAS links.

You redirect the customer to the approve link. After they confirm on the PayPal page, they come back to your return_url. The order is now APPROVED.

Then you POST to the capture link. When that returns COMPLETED, funds have transferred.

IMPORTANT

Never fulfill on APPROVED. The customer has authorized, not paid. Only the PAYMENT.CAPTURE.COMPLETED webhook confirms that money has actually moved. Fulfilling on the return_url or on APPROVED status will cost you inventory.

1

Create Order

POST /api/base/paypal/v2/checkout/orders

Returns order ID + HATEOAS links

2

Customer Approves

Redirect → approve link

Order status moves to APPROVED

3

Capture Payment

POST → capture link

COMPLETED = safe to fulfill

Order status scenarios

Your integration needs to handle all four. Two are critical paths.

01COMPLETED

Capture succeeded. Funds are on the way. This is the only status where it's safe to trigger fulfillment.

critical
02APPROVED

Customer clicked Approve on the PayPal page. The authorization is in place but no money has moved yet. You must still call capture.

critical
03VOIDED

The authorization was cancelled — either you explicitly voided it, or it expired without a capture. Create a fresh order to retry.

04PAYER_ACTION_REQUIRED

Extra customer verification is needed (e.g., 3D Secure). Follow the payer-action HATEOAS link to send them to the authentication page.

Integration code

curl -X POST https://mockgateway.dev/api/base/paypal/v2/checkout/orders \
  -H "Authorization: Bearer YOUR_MOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "intent": "CAPTURE",
    "purchase_units": [{
      "reference_id": "ORD-PP-4455",
      "amount": {
        "currency_code": "USD",
        "value": "99.00"
      },
      "description": "Enterprise plan — annual"
    }],
    "application_context": {
      "return_url": "https://yourapp.com/checkout/return",
      "cancel_url": "https://yourapp.com/checkout/cancel"
    }
  }'

Request parameters

Sent to POST /api/base/paypal/v2/checkout/orders

Request parameters
ParameterTypeRequiredDescription
amountnumberrequired

The total amount for the order

e.g. 100.00
currency_codestringrequired

Three-letter ISO 4217 currency code

e.g. USD
descriptionstringoptional

Purchase description

e.g. Payment for services
reference_idstringoptional

API caller-provided external ID for reference

e.g. my-order-123
invoice_idstringoptional

Your invoice or order reference

e.g. INV-2024-001
custom_idstringoptional

Custom data for your own tracking

e.g. CUST-12345

Response fields

Response fields
FieldTypeDescription
idstringPayPal-generated order ID
statusenumStatus of the PayPal order
payer_idstringPayPal payer ID
payer_emailemailEmail of the PayPal payer
create_timedatetimeOrder creation timestamp
capture_idstringPayPal capture transaction ID

Questions about PayPal Orders API v2

Other gateway templates

Looking for a different provider?