PayPal Direct Mode — Shipping

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

TermMeaning
shipping_preference: SET_PROVIDED_ADDRESSMerchant supplies ship-to via buyer.shipping_details; buyer should not change address
shipping_preference: GET_FROM_FILEBuyer selects or edits address from PayPal profile
buyer.shipping_detailsComplete ship-to (name + address) when merchant knows destination at create
order_update_callback_configHTTPS callback_url + callback_events; PayPal POSTs on address or speed change
SHIPPING_ADDRESSFires on review load and address change; response must include non-empty shipping_options
SHIPPING_OPTIONSFires on shipping method change; subscribe when speed changes need server recalc
user_action: PAY_NOW / CONTINUEReview button copy only — not a separate shipping use case

Flat connection_options.paypal-paypal fields

FieldPayPal behavior
user_actionPAY_NOW vs CONTINUE button emphasis
shipping_preferenceSET_PROVIDED_ADDRESS vs GET_FROM_FILE
order_update_callback_config.callback_urlHTTPS URL for shipping-module updates
order_update_callback_config.callback_eventsSHIPPING_ADDRESS, SHIPPING_OPTIONS, or both
shipping.optionsPrelisted shipping speeds at create (when connector supports)

Use-case summary

Shopper experienceIDAddress at createEditable in PayPalCallbacksTypical create config
Fixed store / pickup — locked ship-to1Yes (buyer.shipping_details)NoNoSET_PROVIDED_ADDRESS
Fixed home — locked ship-to2Yes (customer home)NoNoSET_PROVIDED_ADDRESS
Buyer picks address — static totals3NoYesNoGET_FROM_FILE
Full dynamic shipping module4NoYesSHIPPING_ADDRESS + SHIPPING_OPTIONSGET_FROM_FILE + callbacks + shipping.options
Callback listener implementation5Pattern for 4See §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_ADDRESS
  • buyer.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_config and shipping.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_FILE
  • order_update_callback_config: HTTPS callback_url, callback_events: ["SHIPPING_OPTIONS", "SHIPPING_ADDRESS"]
  • shipping.options: prelisted methods at create

Callback expectations:

  • Review load and address change → SHIPPING_ADDRESS POST
  • Speed change → SHIPPING_OPTIONS POST
  • Response: HTTP 200, updated purchase_units[].amount (with breakdown) and non-empty shipping_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

  1. Ship-to known before PayPal? Store → 1; customer home → 2
  2. Buyer must pick address in PayPal? → 3 or 4
  3. Dynamic tax/shipping on address or speed change? → 4 (HTTPS listener at create)
  4. Static totals acceptable? → 3

Review button variant (PAY_NOW vs CONTINUE)

ValueButtonSDK note
PAY_NOWPay NowOften paired with commit=true
CONTINUEContinueOften 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.