On this page
No Headings
Last updated: June 16, 2026
The shipping module presents shipping details to a buyer during the Pay with PayPal and Pay with Venmo flow. The merchant has several options available for controlling how shipping addresses and shipping options are handled. The server-side shipping callbacks allow you to update the shipping and order amount information as buyers make changes on the review page.
Buyers can use the shipping module to specify the shipping address and shipping options on the review page. The system sends a callback to the merchant's URL with the updated shipping information.
When the buyer provides their shipping address, a callback is sent to the merchant server with the buyer's address using the server-side shipping callbacks. In response, the merchant can send the shipping options and updated order cost amounts.
You can use server-side callbacks to:
While both server-side and client-side callbacks are possible, it is generally recommended to use server-side callbacks, as client-side callbacks may not be available in all situations. For example, client-side callbacks using JS-SDK are designed primarily for web-based integrations and are incompatible with Venmo. They may not be suitable for native mobile applications that often require different APIs or SDKs tailored for mobile platforms.
The following sample PayPal and Venmo review pages show a buyer's shipping address and options:
The shipping address and options callback process involves the following steps:
Use the Orders v2 API Create Order request to set up the flow used by the buyer to approve a transaction. Both Pay with PayPal and Pay with Venmo support the shipping module. The request can utilize either payment_source:paypal or payment_source:venmo. There are several
ways to determine how the shipping address and options are displayed on the review page and which events create callbacks when the buyer interacts with them.
The payment_source.*.experience_context.order_update_callback_config object is used to enable and configure server-side callbacks during the flow. The callback_url field specifies the endpoint on your
servers where PayPal sends the callback request. The callback_events array specifies which callback events you support. The flow supports the following events:
SHIPPING_ADDRESS: This event happens when the review page loads for the first time and when the buyer changes their shipping address. We recommend merchants to subscribe only to SHIPPING_ADDRESS. Thi requires a merchant to respond with all the shipping options and amounts calculated on the initial callback. Since all options have been returned, the merchant callback does not have to be called when the selection option changes. This method reduces delays and the number of requests for PayPal.SHIPPING_OPTIONS: This event happens when the buyer selects new shipping options and when they change shipping options. We recommend merchants subscribe to this if they want to be notified or need to recalculate amounts with their callback URL when a selected shipping option changes for the current address. If SHIPPING_OPTIONS is not needed by the merchant, they should subscribe to SHIPPING_ADDRESS.The shipping preference is set using the payment_source.*.experience_context.shipping_preference field. There are 3 possible values:
GET_FROM_FILE: This default preference retrieves the customer-provided shipping address their wallet.NO_SHIPPING: This option removes the shipping address information from the review page and the API response. However, the shipping.phone_number and shipping.email_address fields are returned for digital goods delivery.SET_PROVIDED_ADDRESS: The review page shows the shipping address provided by the merchant in the create order request. The buyer cannot change this address on the review page. If the merchant does not pass an address, the buyer can choose the address on PayPal.If you do not pass a shipping preference, the default behavior is GET_FROM_FILE. The review page shows the default shipping address from the buyer's wallet. The buyer can select a different shipping address from
their wallet or add a new address. Callbacks happen if the shipping preference is GET_FROM_FILE.
If you have a shipping address from the buyer, pass it to Pay with PayPal or Pay with Venmo on the create order request. If you pass a shipping address with a shipping preference of GET_FROM_FILE, it will display on the review page, where the buyer can change the shipping address. You can use the shipping preference SET_PROVIDED_ADDRESS to prevent the buyer from changing the shipping address on the review page.
If your transaction does not involve shipping, you can pass the shipping preference NO_SHIPPING and no shipping information will be displayed on the review page.
Here is a sample Create Order API request to a merchant with a callback config for Pay with PayPal (payment_source:paypal) and Pay with Venmo (payment_source:venmo).
curl -v -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer ACCESS-TOKEN' \
-H 'PayPal-Partner-Attribution-Id: BN-CODE' \
-H 'PayPal-Auth-Assertion: AUTH-ASSERTION-JWT' \
-H 'PayPal-Request-Id: REQUEST-ID' \
-d '{
"intent": "CAPTURE",
"payment_source": {
"paypal": {
"experience_context": {
"user_action": "PAY_NOW",
"shipping_preference": "GET_FROM_FILE",
"return_url": "https://example.com/returnUrl",
"cancel_url": "https://example.com/cancelUrl",
"order_update_callback_config": {
"callback_events": ["SHIPPING_ADDRESS", "SHIPPING_OPTIONS"],
"callback_url": "https://example.com/orders?cart_id=h98h98h&session_id=89h788fg8"
}
}
}
}, "purchase_units": [{
"reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
"items": [{
"name": "T-Shirt",
"description": "Super Fresh Shirt",
"unit_amount": {
"currency_code": "USD",
"value": "50.00"
},
"quantity": "1",
"category": "PHYSICAL_GOODS",
"sku": "sku01",
"image_url": "https://example.com/static/images/items/1/tshirt_green.jpg",
"url": "https://example.com/url-to-the-item-being-purchased-1",
"upc": {
"type": "UPC-A",
"code": "123456789012"
}
}, {
"name": "Shoes",
"description": "Running, Size 10.5",
"sku": "sku02",
"unit_amount": {
"currency_code": "USD",
"value": "25.00"
},
"quantity": "2",
"category": "PHYSICAL_GOODS",
"image_url": "https://example.com/static/images/items/1/shoes_running.jpg",
"url": "https://example.com/url-to-the-item-being-purchased-2",
"upc": {
"type": "UPC-A",
"code": "987654321012"
}
}],
"amount": {
"currency_code": "USD",
"value": "100.00",
"breakdown": {
"item_total": {
"currency_code": "USD",
"value": "100.00"
}
}
}
}]
}'The Server-Side Callback API request to a merchant is consistent with GET orders and includes selected addresses and options outside of the order object.
The following sample is a server-side callback request to the merchant for a shipping event. The callback identifies the order, shipping_address, shipping_option, and purchase_units.
The initial callback to the merchant does not include the "shipping_option" section.
{
"id": "5O190127TN364715T",
"shipping_address": {
"country_code": "US",
"admin_area_1": "TX",
"admin_area_2": "Dallas",
"postal_code": "75001"
},
"shipping_option": {
"id": "2",
"amount": {
"currency_code": "USD",
"value": "20.00"
},
"type": "SHIPPING",
"label": "Free Shipping"
},
"purchase_units": [{
"reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
"amount": {
"currency_code": "USD",
"value": "100.00"
}
}]
}The merchant response to a server-side callback request is similar to an order. A change to the customer's shipping address or options can affect the following fields within the data structure:
purchase_units[].shipping.options represent the shipping modules associated with the purchase units within the order. The purchase order field includes contact and delivery information, including phone number, account email address, order ID, and reference ID.purchase_units[].amount represents the purchase unit amount in the order.Buyer changes can be managed through one of the following approaches:
When the order request is acceptable, the merchant responds with a success message that contains amount and shipping option changes.
Shipping amount must match the selected shipping option.
If you return purchase_units[].shipping_options then the shipping cost in the breakdown must equal the selected option amount: purchase_units[].amount.breakdown.shipping.value must equal purchase_units[].shipping_options[].amount.value for the option where selected = true.
Purchase unit total must equal the breakdown sum.
purchase_units[].amount.value must equal the sum of the amounts you provide in purchase_units[].amount.breakdown.
Item and tax totals must match your Order/Cart.
purchase_units[].amount.breakdown.item_total and purchase_units[].amount.breakdown.tax_total must match the corresponding item and tax totals in your Order/Cart (the values you calculated for the buyer).
Currency code must match
The currency_code for each corresponding amount in both your Order or Cart and the shipping callback response must be the same. This includes all amounts under purchase_units[].amount (such as value and all breakdown fields) and any shipping_options[].amount.
Here is a sample HTTP 200 OK callback response from a merchant with shipping options and amount updates.
{
"id": "8HFTASDATTV",
"purchase_units": [{
"reference_id": "d9f80740-38f0-11e8-b467-0ed5f89f718b",
"amount": {
"currency_code": "USD",
"value": "105.00",
"breakdown": {
"item_total": {
"currency_code": "USD",
"value": "100.00"
},
"tax_total": {
"currency_code": "USD",
"value": "5.00"
},
"shipping": {
"currency_code": "USD",
"value": "0.00"
}
}
},
"shipping_options": [{
"id": "1",
"amount": {
"currency_code": "USD",
"value": "0.00"
},
"type": "SHIPPING",
"label": "Free Shipping",
"selected": true
}, {
"id": "2",
"amount": {
"currency_code": "USD",
"value": "7.00"
},
"type": "SHIPPING",
"label": "USPS Priority Shipping",
"selected": false
}, {
"id": "3",
"amount": {
"currency_code": "USD",
"value": "10.00"
},
"type": "SHIPPING",
"label": "1-Day Shipping",
"selected": false
}]
}]
}A merchant response can include the fields identified in the following table.
| Field | Description | Date type |
|---|---|---|
id | Identifies the merchant. For example, 8HFTASDATTV. | string |
purchase_units | Purchase unit information. Includes reference_id and amount. | object |
reference_id | Uniquely identifies the purchase transaction. For example, d9f80740-38f0-11e8-b467-0ed5f89f718b. | string |
amount | Purchase unit amount in the order. Includes currency_code,value, and breakdown. | object |
currency_code | Code indicating the amount currency type. For example, USD. | string |
value | String denoting the purchase unit amount in the order, formatted as X.XX. For example, 105.00. | amount |
breakdown | Amount breakdown. Includes item_total, tax_total, and shipping. | object |
item_total | Amount item breakdown. Includes currency_code and value. | object |
value | Amount item value. For example, 100.0. | number |
tax_total | Amount tax breakdown. Includes currency_code and value. | object |
value | String denoting thetax_total amount, formatted as X.XX. For example, 5.00. | number |
shipping | Amount shipping breakdown. Includes currency_code and value. | object |
value | String denoting the value. amount, formatted as X.XX. For example, 0.00.In this example, there are no shipping costs associated with the purchase unit. | number |
shipping_options | Shipping option information. Includes id, amount, type, label, and selected. | object |
selected | Determines which options are selected by default. Possible values are true or false. | boolean |
id | Shipping option identifier. For example, 1. | number |
amount | Amount for the selected shipping option. For example, currency_code and value. | object |
value | String denoting the value. amount for the selected shipping option, formatted as X.XX. For example, 0.00. The value depends upon the selected label. | number |
type | An enum that indicates the type of delivery for the selected option. For example, SHIPPING or PICKUP. | string |
label | Dropdown menu option selected by the buyer. Indicates the type of SHIPPING. For example, Free Shipping. Other labels include USPS Priority Shipping and 1-Day Shipping. | string |
The order amount automatically updates based on the options selected by buyers unless you are subscribed to the shipping_options callback. If you are subscribed to shipping_options, ensure the callback response includes the updated total cost so that the checkout page can be updated. You can use the onShippingOptionsChange() callback to update your own database with the new cost amount.
A merchant can issue a 422 error to decline a callback event. In the response, the merchant should provide a reason why the callback is being declined. The following reasons are supported:
| Callback event type | Error | Description |
|---|---|---|
| Shipping address | ADDRESS_ERROR | Your order can't be shipped to this address. |
| Shipping address | COUNTRY_ERROR | Your order can't be shipped to this country. |
| Shipping address | STATE_ERROR | Your order can't be shipped to this state. |
| Shipping address | ZIP_ERROR | Your order can't be shipped to this zip. |
| Shipping option | METHOD_UNAVAILABLE | The shipping method you selected is unavailable. To continue, choose another way to get your order. |
| Shipping option | STORE_UNAVAILABLE | Part of your order isn't available at this store. |
Here is a sample decline callback from the merchant. In this case, there is an HTTP 422 Unprocessable Entity response, which is a country error.
{
"name": "UNPROCESSABLE_ENTITY",
"details": [{
"issue": "COUNTRY_ERROR"
}]
}