PayPal Direct Mode — Shipping
Ship-to and PayPal Shipping module behavior for Gr4vy PayPal Direct Mode.
Prerequisite: How Gr4vy PayPal Direct Mode works — transaction create, session exchange, and completion handshake.
Shipping settings are sent on POST /transactions via connection_options.paypal-paypal and optional buyer.shipping_details. The connector maps flat fields to PayPal at create time. Post-create PayPal order PATCH is unreliable — plan the use case at create time.
Configuration primitives
| Term | Meaning |
|---|---|
shipping_preference: SET_PROVIDED_ADDRESS | Merchant supplies ship-to via buyer.shipping_details; buyer should not change address |
shipping_preference: GET_FROM_FILE | Buyer selects or edits address from PayPal profile |
buyer.shipping_details | Complete ship-to (name + address) when merchant knows destination at create |
order_update_callback_config | HTTPS callback_url + callback_events; PayPal POSTs on address or speed change |
SHIPPING_ADDRESS | Fires on review load and address change; response must include non-empty shipping_options |
SHIPPING_OPTIONS | Fires on shipping method change; subscribe when speed changes need server recalc |
user_action: PAY_NOW / CONTINUE | Review button copy only — not a separate shipping use case |
Flat connection_options.paypal-paypal fields
| Field | PayPal behavior |
|---|---|
user_action | PAY_NOW vs CONTINUE button emphasis |
shipping_preference | SET_PROVIDED_ADDRESS vs GET_FROM_FILE |
order_update_callback_config.callback_url | HTTPS URL for shipping-module updates |
order_update_callback_config.callback_events | SHIPPING_ADDRESS, SHIPPING_OPTIONS, or both |
shipping.options | Prelisted shipping speeds at create (when connector supports) |
Use-case summary
| Shopper experience | ID | Address at create | Editable in PayPal | Callbacks | Typical create config |
|---|---|---|---|---|---|
| Fixed store / pickup — locked ship-to | 1 | Yes (buyer.shipping_details) | No | No | SET_PROVIDED_ADDRESS |
| Fixed home — locked ship-to | 2 | Yes (customer home) | No | No | SET_PROVIDED_ADDRESS |
| Buyer picks address — static totals | 3 | No | Yes | No | GET_FROM_FILE |
| Full dynamic shipping module | 4 | No | Yes | SHIPPING_ADDRESS + SHIPPING_OPTIONS | GET_FROM_FILE + callbacks + shipping.options |
| Callback listener implementation | 5 | — | — | Pattern for 4 | See §5 |
PAY_NOW vs CONTINUE applies to any use case 1–4 — see Review button variant.
1 — Ship to store (fixed address)
When to use: BOPIS, ship-to-store, curbside pickup at a known location.
Required POST /transactions fields:
connection_options.paypal-paypal.shipping_preference:SET_PROVIDED_ADDRESSbuyer.shipping_details: complete store address (name + address lines)- No
order_update_callback_config
Callback expectations: None.
Limitations: SET_PROVIDED_ADDRESS is intent, not a hard UI lock — validate on your Gr4vy environment. Fields must be on create; post-create PATCH is unreliable.
"buyer": {
"shipping_details": {
"first_name": "Store",
"last_name": "Pickup",
"address": {
"line1": "100 Retail Plaza",
"city": "Orlando",
"state": "FL",
"postal_code": "32801",
"country": "US"
}
}
},
"connection_options": {
"paypal-paypal": {
"user_action": "PAY_NOW",
"shipping_preference": "SET_PROVIDED_ADDRESS"
}
}2 — Ship to home (fixed address)
When to use: Merchant already collected the customer’s home delivery address before PayPal opens.
Required POST /transactions fields: Same mechanism as 1 — SET_PROVIDED_ADDRESS + buyer.shipping_details (customer home). No callbacks.
Callback expectations: None.
Limitations: Same as 1. If the saved address is wrong, there is no in-PayPal correction — restart checkout with an updated address.
3 — Buyer chooses address (static totals)
When to use: Standard checkout where tax/shipping is known at Gr4vy create or acceptable as static totals.
Required POST /transactions fields:
connection_options.paypal-paypal.shipping_preference:GET_FROM_FILE- Omit
order_update_callback_configandshipping.options(unless static methods without callbacks)
Callback expectations: None.
Limitations: Insufficient when tax/shipping must change by jurisdiction (use 4). Without callbacks you cannot decline unsupported destinations in-wallet.
4 — Full shipping module (early speeds + both callbacks)
When to use: Dynamic tax and shipping (rate shopping, jurisdiction tax) with recalc on address and speed change.
Required POST /transactions fields:
shipping_preference:GET_FROM_FILEorder_update_callback_config: HTTPScallback_url,callback_events:["SHIPPING_OPTIONS", "SHIPPING_ADDRESS"]shipping.options: prelisted methods at create
Callback expectations:
- Review load and address change →
SHIPPING_ADDRESSPOST - Speed change →
SHIPPING_OPTIONSPOST - Response: HTTP 200, updated
purchase_units[].amount(withbreakdown) and non-emptyshipping_options
Limitations: Empty shipping_options causes SHIPPING_ADDRESS_CALLBACK_ERROR. See §5 for listener implementation.
"connection_options": {
"paypal-paypal": {
"user_action": "PAY_NOW",
"shipping_preference": "GET_FROM_FILE",
"order_update_callback_config": {
"callback_url": "https://merchant.example/order-update",
"callback_events": ["SHIPPING_OPTIONS", "SHIPPING_ADDRESS"]
},
"shipping": {
"options": [
{
"id": "STANDARD",
"label": "Standard shipping",
"selected": true,
"type": "SHIPPING",
"amount": { "currency_code": "USD", "value": "5.00" }
}
]
}
}
}5 — Callback recalculation with optional PayPal GET
Implementation pattern for 4 listeners — not a separate shopper journey.
Incoming (PayPal → merchant): purchase_units, shipping_address, optional shipping_option, order id. Reference: Receive updated order information via callback URL.
Outgoing (merchant → PayPal): HTTP 200 with updated purchase_units[].amount (breakdown: item_total, tax_total, shipping) and shipping_options (required for SHIPPING_ADDRESS). Decline unsupported destinations with 422.
Optional GET /v2/checkout/orders/{id}: Use when reconciling callback payload with canonical order state or when POST is missing fields. Skip when cart snapshot + callback address are authoritative. OAuth client_credentials on merchant server only.
Choosing a use case
- Ship-to known before PayPal? Store → 1; customer home → 2
- Buyer must pick address in PayPal? → 3 or 4
- Dynamic tax/shipping on address or speed change? → 4 (HTTPS listener at create)
- Static totals acceptable? → 3
Review button variant (PAY_NOW vs CONTINUE)
| Value | Button | SDK note |
|---|---|---|
PAY_NOW | Pay Now | Often paired with commit=true |
CONTINUE | Continue | Often paired with commit=false |
Any shipping use case 1–4 can use either value. Completion timing (Pay Now vs Continue) is covered on the main Direct Mode guide.