> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wizlopay.com/llms.txt
> Use this file to discover all available pages before exploring further.

# PayPal (Existing Order)

> Authorize a PayPal order that your own integration created and had the buyer approve, without Gr4vy creating an order of its own.

PayPal (Existing Order) is a variant of the PayPal connector for merchants who already run their own PayPal integration and want to keep it. You create the order and drive buyer approval with your own PayPal SDK, then hand Gr4vy the approved order to authorize or capture. Everything afterwards — capture, void, refund, transaction sync, webhooks, and settlement reporting — behaves exactly as it does on the standard PayPal connector.

<Note>
  This connector is not generally available. It is enabled per environment on request. If you want the standard flow, where Gr4vy creates the PayPal order as part of creating the transaction, see [PayPal](/connections/payments/paypal-paypal) instead.
</Note>

## When to use it

Use this connector when your PayPal integration relies on parts of PayPal's Orders API that Gr4vy does not map, such as `experience_context` options or the contact module. Because you build the order body yourself, you can use anything the [Orders API](https://developer.paypal.com/docs/api/orders/v2/) accepts without waiting on a Gr4vy release.

Compared to the standard PayPal connector, the ownership is reversed:

| Responsibility           | PayPal                                | PayPal (Existing Order)               |
| ------------------------ | ------------------------------------- | ------------------------------------- |
| Creates the PayPal order | Gr4vy, while creating the transaction | You, before any Gr4vy call.           |
| Buyer approval           | Driven by the `orderId` Gr4vy returns | Driven entirely by your PayPal SDK.   |
| Finalizing               | `GET` the `default_completion_url`    | The transaction creation call itself. |
| Order body               | Built by Gr4vy from the transaction   | Built by you, unrestricted.           |

## Setup

Follow the [PayPal setup instructions](/connections/payments/paypal) before configuring this connector. It takes the same credentials as the standard PayPal connector — client ID, client secret, webhook ID, and the optional BN code and merchant ID — and can run alongside it on the same PayPal account.

Billing and shipping ingestion, tokenization, and settlement reporting are configured the same way. See [PayPal](/connections/payments/paypal-paypal#setup) for those settings.

## Authorizing an approved order

The flow is:

1. Create a PayPal order with your own backend call to PayPal's Orders API.
2. Have the buyer approve it with your existing PayPal JS or native SDK integration.
3. Create the Gr4vy transaction with the approved order ID.

Once the buyer has approved the order, create the transaction with `method` set to `paypaldirectorder` and `order_id` in `connection_options["paypal-paypaldirectorder"]`:

| Field      | Description                                                                                                                                                                                                       |
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `order_id` | The ID of an existing PayPal order that was created and approved by the buyer entirely outside of Gr4vy. Gr4vy retrieves this order and authorizes or captures it directly, without creating an order of its own. |

<Note>
  The order must already be approved by the buyer before you create the transaction. Because approval already happened, there is no `approval_url` to redirect to, and the transaction creation response returns the authorization or capture result directly.
</Note>

<CodeGroup>
  ```csharp C# theme={"system"}
  ConnectionOptions = new Dictionary<string, object>()
  {
    ["paypal-paypaldirectorder"] = new Dictionary<string, object>()
    {
      ["order_id"] = "5O190127TN364715T",
    }
  }
  ```

  ```go Go theme={"system"}
  ConnectionOptions: map[string]interface{}{
    "paypal-paypaldirectorder": map[string]interface{}{
      "order_id": "5O190127TN364715T",
    },
  },
  ```

  ```java Java theme={"system"}
  .connectionOptions(Map.of(
    "paypal-paypaldirectorder", Map.of(
      "order_id", "5O190127TN364715T"
    )
  ))
  ```

  ```php PHP theme={"system"}
  connectionOptions: [
    'paypal-paypaldirectorder' => [
      'order_id' => '5O190127TN364715T',
    ]
  ]
  ```

  ```python Python theme={"system"}
  transaction = client.transactions.create(
    amount=1299,
    currency="USD",
    country="US",
    intent="authorize",
    payment_method={
      "method": "paypaldirectorder",
      "country": "US",
      "currency": "USD",
      "redirect_url": "https://example.com/callback",
    },
    connection_options={
      "paypal-paypaldirectorder": {
        "order_id": "5O190127TN364715T",
      }
    }
  )
  ```

  ```ts TypeScript theme={"system"}
  const transaction = await gr4vy.transactions.create({
    amount: 1299,
    currency: "USD",
    country: "US",
    intent: "authorize",
    paymentMethod: {
      method: "paypaldirectorder",
      country: "US",
      currency: "USD",
      redirectUrl: "https://example.com/callback",
    },
    connectionOptions: {
      "paypal-paypaldirectorder": {
        order_id: "5O190127TN364715T",
      },
    },
  });
  ```
</CodeGroup>

## Validation

Gr4vy reads the order from PayPal before it authorizes or captures anything, and rejects the transaction when the order does not match what you asked for. Each of these returns an error before any authorization is attempted:

* **Missing order ID**: `order_id` is required. This connector only authorizes an existing order, so it never falls back to creating one.
* **Intent mismatch**: The order's intent must match the transaction's `intent`, either `authorize` or `capture`.
* **Multiple purchase units**: The order must contain exactly one purchase unit.
* **Currency mismatch**: The order's currency must match the transaction's currency.
* **Amount mismatch**: The order's total must match the transaction's amount.
* **Order no longer actionable**: An order that PayPal has already completed, or that otherwise no longer offers the matching authorize or capture action, is rejected.

<Warning>
  Only the fields above are checked. Anything else you put on the order, including cart items, is not compared against the transaction, so a Gr4vy transaction can carry different line items from the PayPal order it authorized.
</Warning>

## Reconciliation

Before authorizing, Gr4vy patches its own transaction references onto the order — the transaction's short ID as `custom_id` and its reference as `invoice_id` — so the payment matches up in PayPal reporting and settlement the same way a Gr4vy-created order does. Any value your own integration set in those fields is overwritten.

PayPal rejects this patch for some order states. When that happens Gr4vy logs it and continues to authorize or capture, so the payment still succeeds but that transaction cannot be matched by reference in settlement.

## Tokenization

To store the payment method for future use, the order must have been created with vault attributes. Include them in your own call to PayPal's Orders API:

```json theme={"system"}
{
  "intent": "AUTHORIZE",
  "payment_source": {
    "paypal": {
      "attributes": {
        "vault": {
          "store_in_vault": "ON_SUCCESS",
          "usage_type": "MERCHANT"
        }
      },
      "experience_context": {
        "return_url": "https://example.com/return",
        "cancel_url": "https://example.com/cancel"
      }
    }
  },
  "purchase_units": [
    { "amount": { "currency_code": "USD", "value": "12.99" } }
  ]
}
```

<Warning>
  Vault attributes cannot be added after the order is created, and PayPal does not expose them when Gr4vy reads the order, so Gr4vy cannot tell you up front whether an order can be vaulted. Sending `store: true` for an order created without vault attributes silently does nothing: the authorization succeeds, no error is returned, and no payment method is stored.
</Warning>

Once a payment method is stored, charging it later uses the standard PayPal connector behavior.

## Testing

Use the same PayPal sandbox setup as the standard connector. See [PayPal testing](/connections/payments/paypal-paypal#testing) for creating test buyer accounts.
