# Integrate (/v5/checkout/fx/integrate)



To integrate FXaaS:

1. [Obtain an exchange rate quote](#get-exchange-rate-quote): Use the currency exchange API to retrieve and lock an exchange rate for payment processing.
2. [Create order](#use-fxid-and-create-order): Include FXaaS parameters in your payment method's create order calls. This enables support for local currency payments through the Orders v2 API.
3. [Capture payment](#capture-payment): Include FXaaS parameters in the capture order calls.
4. [Handle market moving events](#special-case-handle-market-moving-events-force-majeure).
5. [Test the FXaaS set-up](#test-fxaas-set-up).

## Before you integrate [#before-you-integrate]

* [Verify your eligibility](/foreign-exchange-as-a-service#eligibility) to integrate FXaaS.
* Review your [FXaaS contract](/foreign-exchange-as-a-service#eligibility). As part of FXaaS onboarding, PayPal enables FXaaS for your account based on the contract. The contract specifies the rate expiration interval, PayPal's rate refresh time, PayPal FX fees, and other relevant terms. For more information about fees, see the [merchant fees page](https://www.paypal.com/business/paypal-business-fees#statement-13).
* Complete [all mandatory steps to get started](/v5/checkout/fx/get-started). Use the retrieved sandbox app credentials (client ID and Secret) in your code to [generate an access token](/api/rest/#3-get-access-token). The access token is a server-side token that helps with app authentication when the app accesses PayPal API resources.

## End-to-end workflow [#end-to-end-workflow]

<img src="https://www.paypalobjects.com/ppdevdocs/FXaaS%20Integrate%20-%20Flow.png" alt="Subscribe,with,card" />

## 1. Get exchange rate quote [#1-get-exchange-rate-quote]

Use the [Currency exchange API](/api/pricing/v2) to retrieve and lock an exchange rate.

> **Note:** **Important:** Your FXaaS contract specifies the expiration time, which determines how long the locked exchange rate remains valid after PayPal updates (refreshes) its exchange rate. At the time of payment processing, PayPal applies the locked exchange rate if it is still within the validity period. If the rate is no longer valid, PayPal processes the transaction using the prevailing exchange rate at the time of processing.

To get an exchange rate quote, you can do one of the following:

* Place multiple API calls to [fetch product-specific quotes](#get-product-specific-quote).
* Place one API call to [retrieve the exchange rate](#get-exchange-rate-only) between a currency pair, which you can cache and apply to multiple products.

### Get a product-specific quote [#get-a-product-specific-quote]

To retrieve a product-specific quote, in server-side code, call the [Quote exchange rates API](/api/pricing/v2/quote-exchange-rates) (`/v2/pricing/quote-exchange-rates`) and include the following parameters in the request payload:

* `base_currency:` Primary holding currency of the account in which PayPal settles money.
* `quote_currency:` Buyer's local currency.
* `base_amount` (optional): Product price in base currency. If this parameter is omitted, PayPal returns only the exchange rate and does not calculate the `quote_amount`.
* `markup_percent` (optional): Markup percentage added to the base exchange rate to include a conversion fee or account for margin adjustments.

**Response:** When the quote is retrieved successfully, the server-side code receives a `200 OK` response. The response payload includes the following parameters:

* `fx_id:` Unique identifier that associates a quoted FX rate with an order.
* `quote_amount.value:` Product price in the buyer's local currency.
* `exchange_rate:` Conversion rate between the base and quote currencies.
* `expiry_time:` UTC timestamp that specifies the validity period of the quoted FX rate.
* `rate_refresh_time:` Fixed daily UTC timestamp that specifies when PayPal updates its exchange rates.

> **Note:** **Important:**
>
> * The retrieved exchange rates are valid for settlement until `rate_refresh_time`. `expiry_time` allows for an additional grace period to guarantee the rate even after `rate_refresh_time`.
> * The time difference between `rate_refresh_time` and `expiry_time` (cut-off period) is set to three hours as a standard. When finalizing the FXaaS contract, you can request a different cut-off period. The request is reviewed and processed on a case-by-case basis.

#### Request

```text lineNumbers
curl -v -X POST 'https://api-m.sandbox.paypal.com/v2/pricing/quote-exchange-rates' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer ACCESS-TOKEN' \
  -d '{
    "quote_items": [
      {
        "base_currency": "USD",
        "quote_currency": "GBP",
        "base_amount": 16.80,
        "markup_percent": 1
      }
    ]
  }'
```

#### Response

```text lineNumbers
HTTP status code: 200
HTTP Headers:
Content-Type": "application/json"
Response payload:
{
  "exchange_rate_quotes": [
    {
      "base_amount": {
        "currency_code": "USD",
        "value": "16.80"
      },
      "quote_amount": {
        "currency_code": "GBP",
        "value": "13.80"
      },
      "exchange_rate": "0.80956391984",
      "fx_id": "MTFFQy05RjkzLTQ4ODkwMjE5LUEzRTAtREQ4OTc1NEQ1NUUw",
      "expiry_time": "2022-03-10T21:30Z",
      "rate_refresh_time": "2022-03-10T18:30Z"
    }
  ]
}
```

### Get exchange rate only [#get-exchange-rate-only]

To retrieve the exchange rate between two currencies, in server-side code, call the [Quote exchange rates API](/api/pricing/v2/quote-exchange-rates) (`/v2/pricing/quote-exchange-rates`) and include only the following parameters in the request payload:

* `base_currency:` Primary holding currency of the account in which PayPal settles money.
* `quote_currency:` Buyer's local currency.

**Response:** When the quote is retrieved successfully, the server-side code receives a `200 OK` response. The response payload includes the following parameters:

* `exchange_rate:` Conversion rate between the base and quote currencies.
* `fx_id:` Unique identifier that links a quoted FX rate to an order.
* `expiry_time:` UTC timestamp that defines how long the quoted FX rate remains valid.
* `rate_refresh_time:` Fixed daily UTC timestamp that defines when PayPal refreshes its exchange rates.

> **Note:** **Important:** The retrieved exchange rate is valid for settlement until `rate_refresh_time`. `expiry_time` allows for an additional grace period to guarantee the rate even after `rate_refresh_time`. The time difference between `rate_refresh_time` and `expiry_time` (cut-off period) is set to three hours as a standard. When finalizing the FXaaS contract, you can request a different cut-off period. The request is reviewed and processed on a case-by-case basis.

#### Request

```text lineNumbers
curl -v -X POST 'https://api-m.sandbox.paypal.com/v2/pricing/quote-exchange-rates' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer ACCESS-TOKEN' \
  -d '{
    "quote_items": [
      {
        "base_currency": "USD",
        "quote_currency": "GBP"
      }
    ]
  }'
```

#### Response

```text lineNumbers
HTTP status code: 200
HTTP Headers:
Content-Type": "application/json"
Response payload:
{
  "exchange_rate_quotes": [
    {
      "base_amount": {
        "currency_code": "USD",
        "value": "1.00"
      },
      "quote_amount": {
        "currency_code": "GBP",
        "value": "0.744"
      },
      "exchange_rate": "0.7444",
      "expiry_time": "2019-02-28T16:00:00Z",
      "rate_refresh_time": "2019-02-28T14:00:00Z",
      "fx_id": "1qids09fnobhufisfno89h00000"
    }
  ]
}
```

#### Proceed as follows [#proceed-as-follows]

* Save `exchange_rate_quotes[].fx_id` and `exchange_rate_quotes[].quote_amount{}`.
* If you only retrieved the exchange rate, use `exchange_rate_quotes[].quote_amount.value` to calculate product prices in the local currency.
* After authenticating the buyer, show the product prices in the local currency.
* Upon checkout, call the Orders v2 API using the stored `fx_id` as `payment_instruction.payee_receivable_fx_rate_id`.

## 2. Use fx\_id and create order [#2-use-fx_id-and-create-order]

When a buyer completes a purchase, use the [Orders v2 API](/api/orders/v2) to initiate payment processing and create an order in the PayPal system. In server-side code, call the [Create order](/api/orders/v2/orders-create) API (`v2/checkout/orders`) and include the following parameter in the request payload to associate the order with the locked exchange rate:

`payment_instruction.payee_receivable_fx_rate_id:` Set the value to the `fx_id` obtained as part of the exchange rate quote.

> **Note:** **Important:** The locked exchange rate is valid for settlement until `rate_refresh_time`. `expiry_time` allows for an additional grace period to guarantee the rate even after `rate_refresh_time`. If the Create order request does not include the `payment_instruction.payee_receivable_fx_rate_id` parameter, during payment capture, PayPal does not consider the additional `expiry_time`. PayPal validates whether the `rate_refresh_time` window is valid. If not, PayPal processes the transaction using the prevailing exchange rate at the time of the transaction.

**Response:** When the order is created successfully, the server-side code receives a `200 OK` response. The response payload includes the following parameters:

* `payee_receivable_fx_rate_id:` Identifier for the quoted FX rate that PayPal associates with the order and applies during capture if the exchange rate is still valid.
* `id:` Unique identifier that PayPal assigns to the created order.
* `links[].href` (with `rel: approve`): URL for payment approval.

#### Request

```text lineNumbers
curl -v -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS-TOKEN" \
-d '{
  "intent": "CAPTURE",
  "purchase_units": [
    {
      "amount": {
        "currency_code": "GBP",
        "value": "13.60"
      },
      "payment_instruction": {
        "payee_receivable_fx_rate_id": "MTFFQy05RjBFLTEyOTlDQTgwLTg3MzMtMzk0ODIwRUEwMTc4"
      }
    }
  ]
}'
```

#### Response

```text lineNumbers
{
  "id": "2E429357R2950060H",
  "intent": "CAPTURE",
  "status": "CREATED",
  "purchase_units": [
    {
      "reference_id": "default",
      "amount": {
        "currency_code": "GBP",
        "value": "13.60"
      },
      "payee": {
        "email_address": "merchant@test.com",
        "merchant_id": "R14RE5TF5HRT5"
      },
      "payment_instruction": {
        "payee_receivable_fx_rate_id": "MTFFQy05RjBFLTEyOTlDQTgwLTg3MzMtMzk0ODIwRUEwMTc4"
      }
    }
  ],
  "create_time": "2022-03-09T06:42:21Z",
  "links": [
    {
      "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/2E429357R2950060H",
      "rel": "self",
      "method": "GET"
    },
    {
      "href": "https://www.sandbox.paypal.com/checkoutnow?token=2E429357R2950060H",
      "rel": "approve",
      "method": "GET"
    },
    {
      "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/2E429357R2950060H",
      "rel": "update",
      "method": "PATCH"
    },
    {
      "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/2E429357R2950060H/capture",
      "rel": "capture",
      "method": "POST"
    }
  ]
}
```

#### Proceed as follows [#proceed-as-follows-1]

* Save the `id` returned in the response. This identifier is required to capture the payment after buyer approval.
* Send the buyer to the approval URL to review and approve the payment.
* After the buyer approves the payment, call the Orders v2 > [Capture payment for an order](/api/orders/v2/orders-capture) API.

## 3. Capture payment [#3-capture-payment]

When a buyer approves the payment for the purchase, use the [Orders v2 API](/api/orders/v2) to finalize the payment and move money from the buyer to PayPal. In server-side code, call the [Capture payment for an order](/api/orders/v2/orders-capture) API (`v2/checkout/orders/:id/capture`). In the request payload, include the `id` retrieved as part of the create order response as a path parameter. PayPal identifies the order and its associated `fx_id` using the path parameter. PayPal determines the locked exchange rate based on the `fx_id`. If the exchange rate is valid, PayPal captures money from the buyer account using the locked exchange rate.

> **Note:** **Important:**
>
> * Capture the completed payment within the FX quote validity window (`expiry_time`) to ensure the locked rate is applied.
> * If the Create order request does not include the `payment_instruction.payee_receivable_fx_rate_id` parameter, during payment capture, PayPal does not consider the additional `expiry_time`. PayPal validates whether the `rate_refresh_time` window is valid. If not, PayPal processes the transaction using the prevailing exchange rate at the time of the transaction.
> * PayPal transaction fee is charged in the buyer (payment) currency.

**Response:** When the order is created successfully, the server-side code receives a `200 OK` response. The response payload includes the following parameters:

* `payments.captures[].seller_receivable_breakdown.receivable_amount:` Final amount the merchant receives in their base currency.
* `payments.captures[].seller_receivable_breakdown.exchange_rate:` Exchange rate PayPal applies to convert the quote currency to your base currency.
* `payments.captures[].seller_receivable_breakdown.paypal_fee:` PayPal's transaction fee specified in terms of the quote currency.
* `payments.captures[].id:` Unique identifier for the captured payment.

#### Request

```text lineNumbers
curl -v -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders/5O190127TN364715T/capture \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ACCESS-TOKEN" \
  -H "PayPal-Request-Id: 7b92603e-77ed-4896-8e78-5dea2050476a"
```

#### Response

```text lineNumbers
{
  "id": "2E429357R2950060H",
  "intent": "CAPTURE",
  "status": "COMPLETED",
  "purchase_units": [
    {
      "reference_id": "default",
      "amount": {
        "currency_code": "GBP",
        "value": "13.60"
      },
      "payee": {
        "email_address": "merchant@test.com",
        "merchant_id": "R14RE5TF5HRT5"
      },
      "payment_instruction": {
        "payee_receivable_fx_rate_id": "MTFFQy05RjBFLTEyOTlDQTgwLTg3MzMtMzk0ODIwRUEwMTc4"
      },
      "soft_descriptor": "PAYPAL *D B S STORE",
      "shipping": {
        "name": {
          "full_name": "John"
        },
        "address": {
          "address_line_1": "2211 N First Street",
          "admin_area_2": "San Jose",
          "postal_code": "95131",
          "country_code": "US"
        }
      },
      "payments": {
        "captures": [
          {
            "id": "3PY98422A48903841",
            "status": "COMPLETED",
            "amount": {
              "currency_code": "GBP",
              "value": "13.60"
            },
            "final_capture": true,
            "seller_protection": {
              "status": "ELIGIBLE",
              "dispute_categories": [
                "ITEM_NOT_RECEIVED",
                "UNAUTHORIZED_TRANSACTION"
              ]
            },
            "seller_receivable_breakdown": {
              "gross_amount": {
                "currency_code": "GBP",
                "value": "13.60"
              },
              "paypal_fee": {
                "currency_code": "GBP",
                "value": "0.66"
              },
              "net_amount": {
                "currency_code": "GBP",
                "value": "12.94"
              },
              "receivable_amount": {
                "currency_code": "USD",
                "value": "16.14"
              },
              "exchange_rate": {
                "source_currency": "GBP",
                "target_currency": "USD",
                "value": "1.247225466845946"
              }
            },
            "links": [
              {
                "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/3PY98422A48903841",
                "rel": "self",
                "method": "GET"
              },
              {
                "href": "https://api-m.sandbox.paypal.com/v2/payments/captures/3PY98422A48903841/refund",
                "rel": "refund",
                "method": "POST"
              },
              {
                "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/2E429357R2950060H",
                "rel": "up",
                "method": "GET"
              }
            ],
            "create_time": "2022-03-09T06:44:55Z",
            "update_time": "2022-03-09T06:44:55Z"
          }
        ]
      }
    }
  ],
  "payer": {
    "name": {
      "given_name": "John",
      "surname": "Doe"
    },
    "email_address": "customer@example.com",
    "payer_id": "QYR5Z8XDVJNXQ",
    "address": {
      "country_code": "US"
    }
  },
  "create_time": "2022-03-09T06:42:21Z",
  "update_time": "2022-03-09T06:44:55Z",
  "links": [
    {
      "href": "https://api-m.sandbox.paypal.com/v2/checkout/orders/2E429357R2950060H",
      "rel": "self",
      "method": "GET"
    }
  ]
}
```

#### Proceed as follows [#proceed-as-follows-2]

Save `payments.captures[].id` and `payments.captures[].seller_receivable_breakdown.receivable_amount` for back-end reporting and reconciliation.

## 4. \[Special case] Handle market-moving events (Force majeure) [#4-special-case-handle-market-moving-events-force-majeure]

Market-moving events, such as natural disasters or political unrest, can lead to high volatility in currency markets and the FXaaS exchange rate lock guarantee may be suspended. In response to these events, PayPal updates the exchange rates and issues a new `fx_id` outside the regular refresh cycle.

If such an event occurs after you retrieve an exchange rate quote and before you create an order, the Create an order API call returns a `422 UNPROCESSABLE_ENTITY` response to the server-side code with the following response parameters:

* `issue:` Value set to `FX_RATE_CHANGE_DUE_TO_MARKET_EVENT` indicating that a market-moving event has caused the locked exchange rate to change.
* `links[].href` with `rel: new_fx_id`: URL to fetch the updated exchange rate quote.

#### Request

```text lineNumbers
curl -v -X POST https://api-m.sandbox.paypal.com/v2/checkout/orders \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer ACCESS-TOKEN" \
  -d '{
    "intent": "CAPTURE",
    "purchase_units": [
      {
        "amount": {
          "currency_code": "GBP",
          "value": "13.60"
        },
        "payment_instruction": {
          "payee_receivable_fx_rate_id": "MTFFQy05RjBFLTEyOTlDQTgwLTg3MzMtMzk0ODIwRUEwMTc4"
        }
      }
    ]
  }'
```

#### Response

```text lineNumbers
{
  "name": "UNPROCESSABLE_ENTITY",
  "message": "The requested action could not be performed, semantically incorrect, or failed business validation.",
  "debug_id": "8872d95df4110",
  "details": [
    {
      "field": "payment_instruction/payee_receivable_fx_rate_id",
      "location": "body",
      "issue": "FX_RATE_CHANGE_DUE_TO_MARKET_EVENT",
      "links": [
        {
          "href": "https://api-m.sandbox.paypal.com/v2/pricing/quote-exchange-rates/MTFFRS1EMDVBLTJDNzFCRUYyLTkzN0MtNDQzQzZDRjY4QjUy",
          "method": "GET",
          "rel": "new_fx_id",
          "encType": "application/json"
        }
      ],
      "description": "The FX rate associated with the specified FX rate ID has been changed due to market events. Please refer to the provided new_fx_id link to retrieve a new FX rate ID and try the request again."
    }
  ],
  "links": [
    {
      "href": "https://developer.paypal.com/api/payments/v2/#error-FX_RATE_CHANGE_DUE_TO_MARKET_EVENT",
      "rel": "information_link",
      "encType": "application/json"
    }
  ]
}
```

#### Proceed as follows [#proceed-as-follows-3]

* To retrieve the new exchange rate quote, place a `GET` call to the `links[].href` URL (the URL with `links[].rel` set as `new_fx_id`).
* Display the new product prices to buyers.
* Upon checkout, call the Orders v2 API using the new `fx_id` as `payment_instruction.payee_receivable_fx_rate_id`.

> **Note:** **Important:** After the API call that returns the `HTTP 422` response code, any further Create an order API call with the old `fx_id` returns a `200 OK` response with `payee_receivable_fx_rate_id` set to the new `fx_id`. This indicates that during payment capture, PayPal processes payments based on the new updated exchange rate (associated with the new `fx_id`).

## 5. Test FXaaS set-up [#5-test-fxaas-set-up]

Before going live, use your sandbox environment to thoroughly test FXaaS integration. This includes verifying the exchange rates retrieval logic, order creation logic, payment capture logic, and error handling.

After testing, [Move your app to production](/api/rest/production/).
