Overview
Integrating Shipping Module
The PayPal shipping module displays shipping details to buyers during the PayPal checkout flow. Merchants can control how shipping addresses and options are handled through several configuration methods.
Using server-side shipping callbacks, merchants can dynamically update shipping details and order amounts as buyers make changes on the PayPal review page.
When buyers select or update their shipping address and option, PayPal sends a callback to the merchant’s server containing the updated shipping information (such as address, state, city, country code, and postal code). In response, the merchant can return updated shipping options and revised order totals to PayPal.
To include Shipping Module in your integration, set the server side shipping callback URL as shown below. For more information, see the Integration code samples section, below.
- javascript
// Add shippingCallbackUrl createPayment() request options
createPaymentRequestOptions.shippingCallbackUrl = "https://www.example.com/"Eligibility
- Supports One-time payments only
- Works with the Pay Now flow
Callback Request to Merchant
When a shipping address or shipping option event occurs, PayPal sends a callback request to the configured shipping CallbackUrl. The callback includes information about the order, shipping address, shipping options, and the number of purchase units.
To include the Shipping Module in your integration, set the server-side shipping callback URL as:
- javascript
// Add shippingCallbackUrl createPayment() request options
createPaymentRequestOptions.shippingCallbackUrl = "https://www.example.com/"Callback Events
The shipping module supports the following callback events:
onShippingAddressChange: This event occurs when the review page loads for the first time and when the buyer changes their shipping address. We recommend subscribing only toonShippingAddressChange, as this requires you to respond with all shipping options and amounts calculated on the initial callback, reducing delays and the number of requests.onShippingOptionsChange: This event occurs when the buyer selects new shipping options. Subscribe to this if you want to be notified or need to recalculate amounts when a selected shipping option changes for the current address.
onShippingAddressChange
- javascript
Updates an order to add shipping address to purchase unit (or) replace the shipping address
//Request
curl -v -X PATCH 'https://api-m.sandbox.paypal.com/v2/checkout/orders/:order_id'
-H "Content-Type: application/json"
-H "Authorization: Bearer ACCESS_TOKEN"
-d '[
{
"op": "replace",
"path": "/purchase_units/@reference_id=='PUHF'/shipping/address",
"value": {
"address_line_1": "2211 N First Street",
"address_line_2": "Building 17",
"admin_area_2": "San Jose",
"admin_area_1": "CA",
"postal_code": "95131",
"country_code": "US"
}
}
]'While the buyer is on the PayPal site, you can update their shopping cart to reflect the shipping address they selected on PayPal. This callback is triggered any time the user selects a new shipping option. You can use the callback to:
- Validate that you support the shipping method.
- Update shipping costs.
- Change the line items in the cart.
- Inform the buyer that you don't support their shipping method.
onShippingOptionsChange
- javascript
Updates an order to add shipping options to purchase unit.
//Request
curl -v -X PATCH 'https://api-m.sandbox.paypal.com/v2/checkout/orders/:order_id'
-H 'Authorization: Bearer ACCESS_TOKEN
-H 'Content-Type: application/json'
-d '[
{
"op": "add",
"path": "/purchase_units/@reference_id=='PUHF'/shipping/options",
"value": [
{
"id": "STORE0002012345202",
"label": "Ground Shipping",
"type": "SHIPPING",
"amount": {
"currency_code": "USD",
"value": "10.00"
},
"selected": true
}
]
}
]'Triggered anytime the user selects a new shipping option:
- Validate that you support the shipping address.
- Update shipping costs.
- Change the line items in the cart.
- Inform the buyer that you don't support their shipping address.
Enabling server-side callbacks
The createPayment({ shippingCallbackUrl: "https://example.com/callback" }) object is used to enable and configure server-side callbacks during the flow. The shippingCallbackUrl field specifies the endpoint on your servers where the callback request is sent. The callback_events array specifies which callback events you support. The flow supports the following events:
- javascript
callbackEvents: ["SHIPPING_ADDRESS"];Or if you want to support both events:
- javascript
callbackEvents: ["SHIPPING_ADDRESS", "SHIPPING_OPTIONS"];Callback Request Fields
| Field | Description | Data Type |
|---|---|---|
| orderId | Unique identifier for the shipping order. For example, 5O190127TN364715T | string |
| shipping_address | Shipping address information | object |
| shipping_address.country_code | Identifies the shipping address country. For example, US | string |
| shipping_address.admin_area_1 | Identifies the shipping address state or region. For example, CA | string |
| shipping_address.admin_area_2 | Identifies the shipping address city or municipality. For example, San Jose | string |
| shipping_address.postal_code | Identifies the postal code. For example, 95131 | string |
| shipping_option | Shipping option information (not included in initial callback) | object |
| shipping_option.selected | Determines which option is selected by default. Possible values are true or false | boolean |
| shipping_option.id | Shipping option identifier. For example, SHIP_123 | string |
| shipping_option.type | Indicates the type of shipping option. For example, SHIPPING | string |
| shipping_option.amount | Shipping option amount information | object |
| shipping_option.amount.currency_code | Indicates the type of currency. For example, USD | string |
| shipping_option.amount.value | String denoting the purchase price and shipping option price, formatted as decimal | string |
| shipping_option.description | Dropdown menu option selected by the buyer. For example, Standard Shipping | string |
| purchase_units | Defines the purchase units | array |
| purchase_units[].reference_id | Identifier associated with the purchase units. For example, PUHF | string |
Merchant Response to Callback
Your server must respond to the callback with updated shipping options and order amounts. The response structure is similar to an order object.
Merchant Success Response
When the callback request is acceptable, respond with an HTTP 200 OK status and include shipping options and amount updates:
- javascript
{
"merchant_id": "2V7XXXX2CXX15924N",
"purchase_units": [
{
"reference_id": "PUHF",
"amount": {
"currency_code": "USD",
"value": "230.00",
"breakdown": {
"item_total": {
"value": "180.00",
"currency_code": "USD"
},
"tax_total": {
"value": "20.00",
"currency_code": "USD"
},
"shipping": {
"value": "30.00",
"currency_code": "USD"
}
}
},
"shipping": {
"options": [
{
"selected": true,
"id": "SHIP_123",
"amount": {
"value": "30.00",
"currency_code": "USD"
},
"type": "SHIPPING",
"label": "Express Shipping"
},
{
"selected": false,
"id": "SHIP_456",
"amount": {
"value": "15.00",
"currency_code": "USD"
},
"type": "SHIPPING",
"label": "Standard Shipping"
}
]
}
}
]
}Merchant Success Response Fields
| Field | Description | Data Type |
|---|---|---|
| merchant_id | Identifies the merchant. For example, 2V7XXXX2CXX15924N | string |
| purchase_units | Purchase unit information | array |
| purchase_units[].reference_id | Uniquely identifies the purchase transaction. For example, PUHF | string |
| purchase_units[].amount | Purchase unit amount in the order | object |
| purchase_units[].amount.currency_code | Code indicating the amount currency type. For example, USD | string |
| purchase_units[].amount.value | String denoting the purchase unit amount in the order, formatted as decimal | string |
| purchase_units[].amount.breakdown | Amount breakdown | object |
| purchase_units[].amount.breakdown.item_total | Amount item breakdown | object |
| purchase_units[].amount.breakdown.item_total.value | Amount item value. For example, 180.00 | string |
| purchase_units[].amount.breakdown.tax_total | Amount tax breakdown | object |
| purchase_units[].amount.breakdown.tax_total.value | String denoting the tax amount, formatted as decimal | string |
| purchase_units[].amount.breakdown.shipping | Amount shipping breakdown | object |
| purchase_units[].amount.breakdown.shipping.value | String denoting the shipping amount, formatted as decimal | string |
| purchase_units[].shipping.options | Shipping option information | array |
| purchase_units[].shipping.options[].selected | Determines which options are selected by default. Possible values are true or false | boolean |
| purchase_units[].shipping.options[].id | Shipping option identifier. For example, SHIP_123 | string |
| purchase_units[].shipping.options[].amount | Amount for the selected shipping option | object |
| purchase_units[].shipping.options[].amount.value | String denoting the amount for the selected shipping option, formatted as decimal | string |
| purchase_units[].shipping.options[].type | An enum that indicates the type of delivery for the selected option. For example, SHIPPING | string |
| purchase_units[].shipping.options[].label | Dropdown menu option selected by the buyer. Indicates the type of shipping method | string |
Merchant Decline Response
Your merchant server can issue an HTTP 422 Unprocessable Entity error to decline a callback event. In the response, provide a reason why the callback is being declined. The following error reasons are supported:
| Callback Event Type | Error | Description |
|---|---|---|
| Shipping address | SHIPPING_ADDRESS_INVALID | Your order can't be shipped to this address. |
| Shipping address | SHIPPING_COUNTRY_UNSUPPORTED | Your order can't be shipped to this country. |
| Shipping address | SHIPPING_STATE_UNSUPPORTED | Your order can't be shipped to this state. |
| Shipping address | SHIPPING_ZIP_UNSUPPORTED | Your order can't be shipped to this zip. |
| Shipping option | SHIPPING_OPTION_NOT_AVAILABLE | The shipping method you selected is unavailable. To continue, choose another way to get your order. |
| Shipping option | PICKUP_NOT_AVAILABLE | Part of your order isn't available at this store. |
Sample decline callback response:
- javascript
"name": "UNPROCESSABLE_ENTITY", "details": [ { "issue": "SHIPPING_COUNTRY_UNSUPPORTED", "description": "The shipping country is not supported" } ], "message": "The requested action could not be performed, semantically incorrect, or failed business validation.", "debug_id": "9e450c2dac87c", "links": [ { "href": "https://developer.paypal.com/docs/api/orders/v2/#error-SHIPPING_COUNTRY_UNSUPPORTED", "rel": "information_link", "method": "GET" } ]Integration notes
- If you are subscribed to shipping_options, ensure the callback response includes the updated total cost so that the checkout page can be updated.
- Merchants with client-side shopping carts should include item details when they create the order for server-side shipping callbacks.
- The cart identifier will be embedded in the callback URL if merchants cannot associate their shopping cart with the order.
Domain registration
In order to use server-side shipping callbacks with PayPal, you'll need to register any domains you plan to use as the shippingCallbackUrl. You can do this through the Braintree control panel using the instructions below. You will have to do this in both your sandbox and production environments.
Rules for domain names
A domain name:
Must not start with a scheme (e.g. "https://").
Must be between 4 and 255 characters.
Cannot contain spaces or wildcards (*).
Can consist of a top-level domain, a second-level domain, and 0 or more subdomains etc.
a.b.example.comwhere example is the top-level domain, com is the second-level domain, and a and b are subdomains.The top-level domain must consist of 2 to 63 letters.
Each subdomain and the second-level domain must consist of 1 to 63 alphanumeric characters and or hyphens.
Each subdomain and the second-level domain can not start or end with a hyphen.
The top-level domain, second-level domain, and the subdomains must be separated by a dot(.).
Can end with a trailing dot.
Sandbox environment
Register your sandbox domain name in the Braintree Control Panel:
Log into your sandbox Control Panel
Click on the gear icon in the top right corner
Click Account Settings from the drop-down menu
Scroll to the Payment Methods section
Next to PayPal, click the Options link
Client the View Domain Names button
Enter the domain of your return page in the Specify Your Domain Names section.
Click Add Domain Names button
Production environment
Register your production domain name in the Braintree Control Panel:
Log into your production Control Panel
Click on the gear icon in the top right corner
Click Account Settings from the drop-down menu
Scroll to the Payment Methods section
Next to PayPal, click the Options link
Client the View Domain Names button
Enter the domain of your return page in the Specify Your Domain Names section.
Click Add Domain Names button
Best practices
- Use
SHIPPING_ADDRESScallbacks only when possible to reduce the number of callback requests and improve performance. - Validate shipping addresses in your callback handler and return appropriate error responses for unsupported locations.
- Calculate accurate shipping costs based on the provided address and return all available shipping options in your response.
- Handle callback timeouts gracefully and ensure your callback endpoint responds quickly to avoid payment flow interruptions.