PayPal
PayPal integration uses the Orders API v2 with a two-step flow: create order → buyer approves → capture.
Getting Started — Step by Step
flowchart TB
A["1. Create PayPal Developer Account"] --> B["2. Create App & Get Keys"]
B --> C["3. Get API Key from Payments Service"]
C --> D["4. Configure PayPal Credentials"]
D --> E["5. Set Webhook URL"]
E --> F["6. Create an Order"]
F --> G["7. Redirect Buyer to PayPal"]
G --> H["8. Capture the Payment"]
style A fill:#e3f2fd
style F fill:#e8f5e9
style H fill:#e8f5e9
Step 1: Create a PayPal Developer Account
- Go to PayPal Developer Dashboard
- Log in or create an account
Step 2: Create an App & Get Keys
- Go to Apps & Credentials
- Click Create App
- Copy your Client ID and Client Secret
- Toggle between Sandbox and Live as needed
Sandbox accounts
PayPal provides sandbox buyer/seller accounts for testing. Find them under Sandbox → Accounts.
Step 3: Get an API Key from Payments Service
Step 4: Configure PayPal Credentials
{
"provider": "paypal",
"paypal": {
"env": "sandbox",
"client_id": "your_paypal_client_id",
"client_secret": "your_paypal_client_secret"
}
}
Step 5: Set Your Webhook URL
Step 6: Create an Order
{
"amount": 25.00,
"currency": "USD",
"provider": "paypal",
"return_url": "https://yourapp.com/payment/success",
"cancel_url": "https://yourapp.com/payment/cancel"
}
Response includes status: "pending" and a provider_ref (PayPal order ID).
Step 7: Redirect Buyer
Redirect the buyer to PayPal's approval page. After they approve, PayPal redirects them to your return_url.
Step 8: Capture the Payment
The payment status changes to completed.
Payment Flow
sequenceDiagram
participant Merchant
participant API as Payments API
participant PayPal
participant Buyer
participant Webhook as Merchant Webhook
Merchant->>API: POST /v1/payments<br/>{ provider: "paypal", return_url, cancel_url }
API->>PayPal: OAuth token (client_credentials)
PayPal-->>API: { access_token: "..." }
API->>PayPal: POST /v2/checkout/orders
PayPal-->>API: { id: "ORDER_ID", status: "CREATED", links: [...] }
API-->>Merchant: { status: "pending", provider_ref: "ORDER_ID" }
Merchant->>Buyer: Redirect to approval_url
Buyer->>PayPal: Approves payment
PayPal->>Buyer: Redirect to return_url
Merchant->>API: POST /v1/payments/{id}/capture
API->>PayPal: POST /v2/checkout/orders/ORDER_ID/capture
PayPal-->>API: { status: "COMPLETED" }
API-->>Merchant: { status: "completed" }
PayPal->>API: POST /v1/webhooks/paypal (callback)
API->>Webhook: { type: "payment.completed" }
Create Payment
Request
{
"amount": 75.00,
"currency": "USD",
"provider": "paypal",
"return_url": "https://merchant.com/success",
"cancel_url": "https://merchant.com/cancel"
}
| Field | Type | Required | Description |
|---|---|---|---|
amount |
decimal | ✅ | Amount to charge |
currency |
string | ✅ | 3-letter ISO code |
provider |
string | ✅ | Must be paypal |
return_url |
string | ✅ | Where buyer returns after approval |
cancel_url |
string | ✅ | Where buyer returns if they cancel |
Response
{
"success": true,
"data": {
"id": "txn-uuid-...",
"merchant_id": "m1m2m3-...",
"amount": "75.00",
"currency": "USD",
"provider": "paypal",
"status": "pending",
"provider_ref": "5O190127TN364715T",
"idempotency_key": "order-789",
"created_at": "2025-01-01T12:00:00Z"
}
}
Getting the approval URL
The approval_url is in the raw provider response. Use GET /v1/payments/{id} if you need to retrieve it later.
Capture Payment
After the buyer approves on PayPal, capture the order:
Response
{
"success": true,
"data": {
"id": "txn-uuid-...",
"status": "completed",
"provider_ref": "5O190127TN364715T",
"amount": "75.00",
"currency": "USD",
"provider": "paypal",
"created_at": "2025-01-01T12:00:00Z"
}
}
Refund
| Field | Type | Required | Description |
|---|---|---|---|
payment_id |
UUID | ✅ | Original payment to refund |
amount |
decimal | ❌ | Partial amount. Omit for full refund |
Response
{
"success": true,
"data": {
"id": "refund-uuid-...",
"payment_id": "txn-uuid-...",
"status": "completed",
"provider_ref": "REFUND_ID",
"amount": "30.00",
"created_at": "2025-01-01T12:00:00Z"
}
}
Inbound Webhook (PayPal → Us)
PayPal sends events to POST /v1/webhooks/paypal with transmission headers.
Verification headers:
| Header | Description |
|---|---|
Paypal-Transmission-Id |
Unique transmission ID |
Paypal-Transmission-Time |
Timestamp |
Paypal-Webhook-Id |
Webhook configuration ID |
Paypal-Transmission-Sig |
HMAC signature |
Events handled:
| PayPal Event | Our Status |
|---|---|
PAYMENT.CAPTURE.COMPLETED |
completed |
PAYMENT.CAPTURE.DENIED |
failed |
PAYMENT.CAPTURE.REFUNDED |
refunded |
PayPal Status Mapping
| PayPal Status | Our Status |
|---|---|
COMPLETED |
completed |
APPROVED |
pending |
CREATED |
pending |
VOIDED |
failed |
PAYER_ACTION_REQUIRED |
pending |
Viewing PayPal Transactions
List and filter all PayPal transactions:
GET /v1/payments?provider=paypal&status=completed
GET /v1/payments?provider=paypal¤cy=USD&min_amount=50
GET /v1/payments?provider=paypal&since=2025-06-01T00:00:00Z
See Payments API → List & Filter for all available filters.