# Use PayPal

```yaml
capability_id: paypal:use
display_name: Use PayPal
ui_capability_key: use_paypal
scope: paypal:use
endpoint:
  method: POST
  path: /paypal/request
operation: paypal.request
billable: true
environments:
  - production
  - staging
  - development
```

## Purpose and authorization

`paypal:use` authorizes an authenticated, application-scoped relay for
supported native PayPal REST API requests. The application MUST use
[Contract v1](https://docs.getgateman.com/contract-v1/) and the `paypal:use`
scope.

Gateman authenticates and authorizes the application, selects configured
PayPal credentials for the requested environment, obtains or reuses a PayPal
OAuth access token, executes the native request, returns the provider response
within this transport contract, and accounts for the admitted operation.
Credentials and access tokens are not returned to callers. This capability
does not imply `secrets:read`.

This is a native API relay, not a complete payment-processing or
merchant-management application. PayPal remains authoritative for native
permissions, resource states, and payment processing.

## Implementation contract

The outer request is an authenticated Gateman JSON request. Its body contains
the nested native PayPal request; the outer envelope is not sent to PayPal.

```http
POST /paypal/request
Content-Type: application/json
X-GM-App: app_example
X-GM-Ts: 1791748800
X-GM-Sig: <Contract-v1-signature>
X-GM-Idempotency-Key: optional-gateman-key
```

Example native Orders request with fictitious data:

```json
{
  "environment": "staging",
  "method": "POST",
  "path": "/v2/checkout/orders",
  "headers": {
    "PayPal-Request-Id": "example-order-001",
    "Prefer": "return=representation"
  },
  "body": {
    "intent": "CAPTURE",
    "purchase_units": [{"amount": {"currency_code": "USD", "value": "10.00"}}]
  }
}
```

PayPal still validates native operation requirements, account permissions, and
resource state.

### Request envelope

| Field | Required | Contract |
| --- | --- | --- |
| `environment` | Yes | String, trimmed and lowercased; `production`, `staging`, or `development`. |
| `path` | Yes | Relative string beginning `/v1/` or `/v2/`. |
| `method` | No | Native method; defaults to `POST`; `GET`, `POST`, `PUT`, `PATCH`, or `DELETE`. |
| `body` | No | Array/object serialized as JSON, or a JSON string; omitted/null becomes empty. |
| `headers` | No | Map of supported native headers; omitted/null becomes empty. |

Unknown top-level fields are currently ignored; integrations SHOULD send only
documented fields. Invalid outer JSON, environment, method, path, body, or
headers is rejected before the provider request. There is no distinct
PayPal-specific total outer-envelope limit.

## Request methods and paths

The nested method MUST be `GET`, `POST`, `PUT`, `PATCH`, or `DELETE`; the
default is `POST`. The path MUST begin with `/v1/` or `/v2/`, be no more than
2,048 bytes, and MAY include a query string. Absolute URLs, schemes, hosts,
control characters, spaces, `#`, `<`, `>`, quotation marks, backslashes, `..`,
and `//` are rejected. `/v1/oauth2/token` cannot be called as a customer relay
path.

Gateman selects a fixed PayPal origin by environment. It does not maintain a
business-resource allowlist and does not guarantee every PayPal operation
works through this JSON transport. There is no general multipart/file-upload
contract, arbitrary binary request-body contract, public streaming-response
interface, automatic pagination, or higher-level payment orchestration.

## Native PayPal JSON serialization

- Arrays and objects are JSON-encoded.
- Omitted/null body and empty string become an empty provider body.
- A non-empty string MUST contain valid JSON and is forwarded unchanged.
- Other scalar body values are invalid.
- The serialized nested body MUST be no larger than 4 MiB (4,194,304 bytes).
- A non-empty body without `Content-Type` receives `application/json`.
- A supplied supported `Content-Type` is preserved.

JSON Patch arrays are representable where the selected PayPal operation expects
them. PayPal validates native fields and business semantics. An invalid or
oversized nested body returns HTTP 400 before operation reservation.

## Native request headers

Only these caller-provided nested headers are supported:

- `Content-Type`
- `Accept`
- `PayPal-Request-Id`
- `Prefer`

Names are matched case-insensitively. Values MUST be strings of no more than
512 bytes and MUST NOT contain CR or LF. Unsupported headers are rejected,
including `Authorization`, `PayPal-Auth-Assertion`, and
`PayPal-Partner-Attribution-Id`. Gateman constructs `Authorization: Bearer`
from its internally obtained token.

## Environment and credential selection

| Gateman environment | PayPal API origin | Credential selected |
| --- | --- | --- |
| `production` | `https://api-m.paypal.com` | Configured production client ID and secret |
| `staging` | `https://api-m.sandbox.paypal.com` | Configured Sandbox client ID and secret |
| `development` | `https://api-m.sandbox.paypal.com` | Configured Sandbox client ID and secret |

Staging and development make genuine PayPal Sandbox requests and may create or
modify Sandbox resources. They are not local simulation modes. The selected
credentials establish the PayPal account context; Gateman does not provide a
merchant selector, provision business accounts, or connect arbitrary sellers.

## OAuth token handling and failure boundaries

Gateman uses PayPal client-credentials OAuth, reuses an eligible cached token,
securely caches tokens by application and environment, obtains a replacement
when needed, and invalidates its cached token after a customer API HTTP 401.
It does not replay that customer request and never exposes the token.

OAuth failure is distinct from customer API failure. Credential or OAuth
rejection may prevent the customer request. OAuth rate limiting can return
HTTP 429; OAuth service failure can return HTTP 503; OAuth transport failure
returns HTTP 502; OAuth rejection can return HTTP 502. When these occur after
reservation, the admitted operation is consumed.

## Native response contract and size limits

Within the limit, Gateman preserves PayPal's status and body and forwards only
these selected headers: `Content-Type`, `Location`, `Retry-After`,
`PayPal-Debug-Id`, `PayPal-Request-Id`, `Correlation-Id`, and `X-Request-Id`.

Native 201, 204, 4xx, and 5xx responses, empty bodies, and non-JSON bodies are
preserved. Redirects are not followed. Gateman does not wrap ordinary PayPal
responses in an `ok` envelope and does not forward unlisted headers. Official
clients may decode responses differently.

### Decoded response-body limit

The maximum decoded customer response is **4 MiB (4,194,304 bytes)**, measured
after HTTP decompression and regardless of PayPal status. Exactly 4,194,304
bytes is accepted. A larger compressed, uncompressed, or unknown-length body
is rejected while received, before full materialization, with no partial body:

```http
HTTP/1.1 502 Bad Gateway
Content-Type: application/json; charset=utf-8
```

```json
{"ok":false,"error":"paypal_response_too_large"}
```

This is a Gateman transport error, not a PayPal rejection. PayPal may already
have processed a mutation, so it is not a safe-to-retry signal. The separate
outbound nested request-body limit is also 4 MiB; neither limit is an explicit
outer-envelope limit.

## Idempotency and retry obligations

The optional outer `X-GM-Idempotency-Key` participates in Contract v1 signing,
but does not create a PayPal relay idempotency record, deduplicate execution,
replay responses, or prevent additional operation accounting.

The nested `PayPal-Request-Id` is forwarded and provides PayPal-native
idempotency where supported by the selected operation. Its semantics and
retention are PayPal-defined, and Gateman does not generate it. It is not
universal protection for every method or resource.

Gateman does not automatically retry customer requests. Repeated invocations
may reach PayPal again and consume separate Gateman operations. After a
timeout, transport failure, missing response, or oversized response, the
application MUST NOT assume a mutation did not execute. Use native
`PayPal-Request-Id` where supported and reconcile before retrying; do not
blindly retry payments, captures, or refunds. See
[Idempotency](https://docs.getgateman.com/idempotency/).

## Operation accounting and concurrency

The billable operation is `paypal.request`. One operation is reserved for one
admitted Gateman relay invocation. Authentication, validation, admission
rejection, and monthly allowance rejection before reservation do not consume an
operation. After reservation, missing credentials, OAuth errors, native PayPal
errors, rate limits, transport failures, timeouts, and oversized responses
consume the reserved operation. A repeated admitted invocation consumes a
separate operation.

Admission rejection may return HTTP 429 with applicable retry guidance;
admission-control unavailability returns HTTP 503. Monthly allowance
exhaustion returns HTTP 403, and accounting unavailability returns HTTP 503.
Native PayPal HTTP 429 is provider-originated and distinct from Gateman
admission errors. PayPal's native `Retry-After` is forwarded when supplied.

## Errors and uncertain outcomes

| Condition | HTTP response | Consequence |
| --- | ---: | --- |
| Contract v1 authentication failure or missing scope | `401` | Gateman rejection; no operation. |
| Invalid JSON, environment, path, method, body, or headers | `400` | Validation rejection; no operation. |
| Oversized nested request body | `400` | Validation rejection; no operation. |
| Concurrency rejection | `429` with applicable guidance | Gateman admission rejection; no operation. |
| Admission guard unavailable | `503` | No operation reserved. |
| Monthly allowance exhausted | `403` | No new operation reserved. |
| Accounting unavailable | `503` | No confirmed operation; PayPal execution is not established. |
| Missing credentials after reservation | `502` | Operation consumed; customer execution not established. |
| OAuth rejection/transport failure | `502` | Operation consumed; customer execution not established. |
| OAuth rate limiting | `429` | Operation consumed; follow OAuth guidance. |
| OAuth service failure | `503` | Operation consumed; customer execution not established. |
| Native PayPal response within limit | PayPal's status/body | Operation consumed; interpret PayPal response. |
| Native PayPal 401 | Native `401` | Operation consumed; cache invalidated; no replay. |
| Native PayPal 429 | Native `429` | Operation consumed; follow PayPal guidance. |
| Customer timeout/transport failure | `502` | Operation consumed; execution may be uncertain. |
| Decoded response over 4 MiB | `502`, `paypal_response_too_large` | Operation consumed; no partial body; execution may be uncertain. |
| Unexpected post-reservation exception | `502`, `PayPal provider unavailable` | Operation is consumed; PayPal execution may be uncertain. |

See [Errors and responses](https://docs.getgateman.com/errors-and-responses/)
and [Authentication](https://docs.getgateman.com/authentication/) for shared
rules. Ordinary PayPal responses remain native.

## Payment lifecycle and webhook responsibilities

Use PayPal does not provide checkout UI, approval handling, order orchestration,
capture/refund reconciliation, subscription activation, vaulting, settlement
reporting, generic payment-state persistence, merchant selection, or payment
completion guarantees. A successful API response is not necessarily proof of
approval, capture, settlement, or fulfilment; applications interpret native
PayPal states.

Gateman does not provision or receive PayPal webhooks through this capability.
Applications configure subscriptions, receive and authenticate events, handle
duplicates and retries, persist outcomes, and reconcile payment state. Gateman
Stripe webhook provisioning does not apply to PayPal. See [Webhooks and event
handling](https://docs.getgateman.com/webhooks-and-event-handling/).

## Official clients and WordPress boundary

The official PHP, Node.js, Python, and Go clients can invoke `/paypal/request`
through generic authenticated request methods. They handle Contract v1 signing
and configured environment injection and do not automatically retry PayPal
requests. No dedicated PayPal helper or canonical client is required.

The current WordPress plugin has no dedicated PayPal checkout or payment
gateway integration through Gateman. Its Stripe payment gateways do not
provide PayPal functionality.

## Data handling and retention

Gateman stores application-scoped PayPal client credentials and securely caches
OAuth tokens for reuse and expiry handling. Tokens are not exposed to callers.
Native request and response bodies are not intentionally persisted by the
relay. Gateman has no PayPal-specific transaction ledger or response-replay
cache; operation accounting is separate from PayPal business state, and
diagnostics omit credentials and payloads. Provider retention belongs to
PayPal. See [Data retention](https://docs.getgateman.com/data-retention/).

## Implementation outcome

A correct integration authenticates with Contract v1 and `paypal:use`, targets
`POST /paypal/request`, selects the intended environment, supplies valid native
JSON and supported headers, relies on Gateman OAuth, preserves native statuses
and bodies, distinguishes OAuth from customer failures, uses
`PayPal-Request-Id` where appropriate, does not mistake `X-GM-Idempotency-Key`
for provider idempotency, handles uncertain/oversized responses without unsafe
retries, accounts for one `paypal.request` operation per admitted invocation,
and manages payment state and webhooks independently.

## Implementation instructions for agents

- **SHOULD** use an official Gateman PHP, Node.js, Python, or Go client where
  suitable; direct HTTP **MUST** follow Contract v1.
- **MUST** use `paypal:use`, `POST /paypal/request`, and a supported environment.
- **MUST** use only the supported methods and relative `/v1/` or `/v2/` paths.
- **MUST NOT** call PayPal OAuth through the public relay.
- **MUST** provide valid native JSON and use only supported nested headers.
- **MUST NOT** override provider Authorization.
- **MUST** use `PayPal-Request-Id` where native idempotency is required and
  supported, and **MUST NOT** rely on `X-GM-Idempotency-Key` for deduplication.
- **MUST** distinguish OAuth/customer errors and **MUST NOT** assume automatic
  retries or blindly retry uncertain mutations.
- **MUST** respect both 4 MiB limits; an oversized decoded response returns
  HTTP 502 `paypal_response_too_large` without partial content.
- **MUST** account for one `paypal.request` operation per admitted invocation.
- **MUST NOT** assume Gateman manages webhooks, settlement, or final payment
  state, or alter credentials/payment workflows without authorization.

## Reusable AGENTS.md block

```markdown
<!-- gateman:begin capability=paypal:use -->
- Use authenticated Contract v1 with `paypal:use` and target `POST /paypal/request`.
- Include `environment`: `production` uses configured PayPal live credentials;
  `staging` and `development` use configured PayPal Sandbox credentials and
  make real Sandbox requests.
- Use only `GET`, `POST`, `PUT`, `PATCH`, or `DELETE` with a relative `/v1/` or
  `/v2/` path. Do not call PayPal OAuth through the relay.
- Represent the native body as JSON. The nested request and decoded response
  bodies are each limited to 4 MiB (4,194,304 bytes).
- Use only `Content-Type`, `Accept`, `PayPal-Request-Id`, and `Prefer` as
  nested headers. Gateman manages application-scoped OAuth bearer access.
- Use `PayPal-Request-Id` for provider idempotency where supported. Do not
  treat `X-GM-Idempotency-Key` as PayPal deduplication; Gateman does not retry
  PayPal requests automatically.
- Account for one `paypal.request` operation per admitted invocation, including
  provider errors, transport failures, and oversized responses.
- A decoded response over 4 MiB returns HTTP 502
  `paypal_response_too_large` without partial content; PayPal may nevertheless
  have executed the request, so this is not a safe-to-retry signal.
- Preserve and interpret native PayPal status/body and distinguish OAuth from
  customer API failures. Do not blindly retry uncertain mutations.
- Payment lifecycle, reconciliation, webhook configuration, receipt, and
  verification remain customer application responsibilities.
- Full capability reference: https://docs.getgateman.com/capabilities/paypal.use/
<!-- gateman:end capability=paypal:use -->
```

## Related references

- [Contract v1](https://docs.getgateman.com/contract-v1/)
- [Authentication](https://docs.getgateman.com/authentication/)
- [Environments](https://docs.getgateman.com/environments/)
- [Idempotency](https://docs.getgateman.com/idempotency/)
- [Operation accounting](https://docs.getgateman.com/operation-accounting/)
- [Rate limits and concurrency](https://docs.getgateman.com/rate-limits-and-concurrency/)
- [Errors and responses](https://docs.getgateman.com/errors-and-responses/)
- [Data retention](https://docs.getgateman.com/data-retention/)
- [Webhooks and event handling](https://docs.getgateman.com/webhooks-and-event-handling/)
- [Official PHP client](https://docs.getgateman.com/canonical-client-php/)
- [Official Node.js client](https://docs.getgateman.com/canonical-client-node/)
- [Official Python client](https://docs.getgateman.com/canonical-client-python/)
- [Official Go client](https://docs.getgateman.com/canonical-client-go/)
