# Pay in 3 Integration Guide
div
div
⏱ 8 min
div
👤 Product Team
###### Technical integration guidance for the Ratepay Pay in 3 payment method
## Overview
This guide explains how to integrate Ratepay Pay in 3 across the buyer journey.
It covers:
- how to calculate Pay in 3 plan values
- how to display Pay in 3 on product, cart, and checkout pages
- how to connect the checkout flow to Ratepay APIs
- when to use client-side calculation and when to use `CALCULATION_REQUEST`
This page focuses on technical integration. For mandatory legal disclosure requirements, see [Pay in 3 legal requirements](/docs/legal/a_legal_requirements_for_the_integration/pay_in_3_integration).
**Recommended approach:** calculate Pay in 3 plan details client-side for display purposes, and use the standard Ratepay checkout API flow for payment processing.
## Integration summary
| Topic | Recommended approach | Alternative approach |
| --- | --- | --- |
| Product page plan display | Client-side calculation | `CALCULATION_REQUEST` |
| Cart page plan display | Client-side calculation | `CALCULATION_REQUEST` |
| Checkout plan display | Client-side calculation | `CALCULATION_REQUEST` |
| Payment processing | `PAYMENT_INIT` + `PAYMENT_REQUEST` | Same |
| Final order confirmation | `PAYMENT_CONFIRM`, if required for your setup | Same |
## Visual overview
The examples below illustrate the main integration contexts and UI states in the buyer journey.
| Screen | Stage | Purpose | Notes |
| --- | --- | --- | --- |
| | Product page | Show Pay in 3 teaser near the product price | Optional from a legal perspective, but recommended for transparency and conversion |
| | Cart page | Recalculate and show Pay in 3 based on the current cart total | Update whenever quantity or cart contents change |
| | Product / Cart | Show detailed plan values in a modal or detail view | Typically opened through a “Learn more” interaction |
| | Checkout | Show Pay in 3 as the selected payment method in the default checkout state | Example of the main payment method view before expanding additional instalment details |
| | Checkout | Show the expanded checkout state with additional instalment breakdown details | Example of an expanded buyer-facing checkout view |
## Core credit terms to show
| Label in UI | German label | Description |
| --- | --- | --- |
| **Per month** | Monatliche Rate | Monthly instalment amount |
| **Duration** | Laufzeit | Number of instalments and duration |
| **Effective Interest** | Effektiver Jahreszins | Annual percentage rate of charge (APR) |
| **Interest** | Zinsbetrag | Total interest amount |
| **Total** | Gesamtbetrag | Total amount payable |
| Monthly instalment amount | Monatliche Rate | Regular instalment amount shown to the buyer |
| Final instalment amount | Letzte Rate | Final instalment amount, which may differ slightly because of rounding |
For Pay in 3, these values are typically:
- **Duration** = `3 months`
- **Effective Interest** = `0%`
- **Interest** = `0`
- **Total** = basket amount
- **Monthly instalment amount** = regular instalment amount calculated from the basket amount
- **Final instalment amount** = last instalment amount, which may differ slightly because of rounding
## Integration
### 1. Choose your calculation approach
Before showing Pay in 3 to the buyer, you need to obtain the plan values for the current amount.
#### Option A — Client-side calculation
This is the recommended approach for display purposes.
Pay in 3 is a fixed product:
- `3` monthly instalments
- `0%` interest
- `0` fees
Because most parameters are static, the display values can be calculated client-side from the basket amount. The final instalment absorbs any rounding remainder.
The calculation function can also accept an optional `shipmentDate` (`YYYY-MM-DD`). If your shop has a reliable delivery estimate, you can use it to generate a more accurate estimated instalment schedule. If omitted, the function uses today’s date and returns a general estimate.
Use the following helper function to calculate Pay in 3 plan values directly in your frontend or backend application code.
```js calculatePayIn3.js
function calculatePayIn3(amount, shipmentDate) {
// --- Configuration: Pay in 3 is a 0% interest, 0-fee product ---
const NUMBER_OF_INSTALLMENTS = 3;
const DURATION_UNIT = "months";
const INTEREST_RATE = 0;
const INTEREST_AMOUNT = 0;
const FEES_AMOUNT = 0;
const HAS_INTEREST = false;
const HAS_FEES = false;
// --- Calculate installment amounts ---
// Split evenly; any rounding remainder goes to the final installment
const regularAmount = Math.round((amount / NUMBER_OF_INSTALLMENTS) * 100) / 100;
const finalAmount = Math.round((amount - regularAmount * (NUMBER_OF_INSTALLMENTS - 1)) * 100) / 100;
// --- Determine the first due date ---
// First instalment is due on the 2nd of the month following shipment,
// provided there are at least 28 days between shipment and that date.
// Otherwise, it is pushed to the 2nd of the subsequent month.
function getFirstDueDate(shipment) {
let next2nd = new Date(Date.UTC(shipment.getUTCFullYear(), shipment.getUTCMonth() + 1, 2));
const shipmentUTC = Date.UTC(shipment.getUTCFullYear(), shipment.getUTCMonth(), shipment.getUTCDate());
const next2ndUTC = Date.UTC(next2nd.getUTCFullYear(), next2nd.getUTCMonth(), next2nd.getUTCDate());
const diffDays = Math.floor((next2ndUTC - shipmentUTC) / (1000 * 60 * 60 * 24));
if (diffDays >= 28) return next2nd;
return new Date(Date.UTC(shipment.getUTCFullYear(), shipment.getUTCMonth() + 2, 2));
}
// Parse shipmentDate string (YYYY-MM-DD) or default to today, using UTC for consistency
let shipment;
if (shipmentDate) {
const parts = shipmentDate.split("-");
shipment = new Date(Date.UTC(+parts[0], +parts[1] - 1, +parts[2]));
} else {
const today = new Date();
shipment = new Date(Date.UTC(today.getFullYear(), today.getMonth(), today.getDate()));
}
const firstDue = getFirstDueDate(shipment);
// --- Build the installment schedule ---
const schedule = [];
let remaining = amount;
for (let i = 1; i <= NUMBER_OF_INSTALLMENTS; i++) {
const dueDate = new Date(Date.UTC(firstDue.getUTCFullYear(), firstDue.getUTCMonth() + (i - 1), 2));
const payment = i < NUMBER_OF_INSTALLMENTS ? regularAmount : finalAmount;
remaining = Math.round((remaining - payment) * 100) / 100;
schedule.push({
installmentNumber: i,
estimatedDueDate:
dueDate.getUTCFullYear() + "-" +
String(dueDate.getUTCMonth() + 1).padStart(2, "0") + "-" +
String(dueDate.getUTCDate()).padStart(2, "0"),
paymentAmount: payment,
principalAmount: payment,
interestAmount: INTEREST_AMOUNT,
feesAmount: FEES_AMOUNT,
remainingPrincipalBalanceAmount: remaining,
});
}
// --- Return the complete plan object ---
return {
planMetadata: { hasInterest: HAS_INTEREST, hasFees: HAS_FEES },
repaymentPlan: {
numberOfInstallments: NUMBER_OF_INSTALLMENTS,
durationOfInstallments: DURATION_UNIT,
regularInstallmentAmount: regularAmount,
finalInstallmentAmount: finalAmount,
},
creditTerms: {
totalPayableAmount: amount,
totalCostOfCreditAmount: INTEREST_AMOUNT + FEES_AMOUNT,
totalInterestAmount: INTEREST_AMOUNT,
totalFeesAmount: FEES_AMOUNT,
nominalInterestRatePercentage: INTEREST_RATE,
annualPercentageRate: INTEREST_RATE,
},
fees: { serviceFeeAmount: FEES_AMOUNT },
installmentSchedule: schedule,
};
}
```
```json example-output.json
{
"planMetadata": {
"hasInterest": false,
"hasFees": false
},
"repaymentPlan": {
"numberOfInstallments": 3,
"durationOfInstallments": "months",
"regularInstallmentAmount": 66.67,
"finalInstallmentAmount": 66.66
},
"creditTerms": {
"totalPayableAmount": 200,
"totalCostOfCreditAmount": 0,
"totalInterestAmount": 0,
"totalFeesAmount": 0,
"nominalInterestRatePercentage": 0,
"annualPercentageRate": 0
},
"fees": {
"serviceFeeAmount": 0
},
"installmentSchedule": [
{
"installmentNumber": 1,
"estimatedDueDate": "2026-05-02",
"paymentAmount": 66.67,
"principalAmount": 66.67,
"interestAmount": 0,
"feesAmount": 0,
"remainingPrincipalBalanceAmount": 133.33
},
{
"installmentNumber": 2,
"estimatedDueDate": "2026-06-02",
"paymentAmount": 66.67,
"principalAmount": 66.67,
"interestAmount": 0,
"feesAmount": 0,
"remainingPrincipalBalanceAmount": 66.66
},
{
"installmentNumber": 3,
"estimatedDueDate": "2026-07-02",
"paymentAmount": 66.66,
"principalAmount": 66.66,
"interestAmount": 0,
"feesAmount": 0,
"remainingPrincipalBalanceAmount": 0
}
]
}
```
Example function usage
```js productPage.js
const productAmount = 200;
const plan = calculatePayIn3(productAmount);
```
```js cartPage.js
const cartTotal = 200;
const plan = calculatePayIn3(cartTotal);
```
```js checkout.js
const basketTotal = 200;
const plan = calculatePayIn3(basketTotal);
```
```js checkoutWithShipmentDate.js
const basketTotal = 200;
const planWithDate = calculatePayIn3(basketTotal, "2026-03-15");
```
#### Key output fields
| JSON field | UI label | Meaning |
| --- | --- | --- |
| `repaymentPlan.regularInstallmentAmount` | **Per month** | Regular monthly instalment amount |
| `repaymentPlan.numberOfInstallments` + `repaymentPlan.durationOfInstallments` | **Duration** | Number and duration of instalments |
| `creditTerms.annualPercentageRate` | **Effective Interest** | Effective interest rate shown to the buyer |
| `creditTerms.totalInterestAmount` | **Interest** | Total interest amount |
| `creditTerms.totalPayableAmount` | **Total** | Total amount payable |
| `repaymentPlan.finalInstallmentAmount` | **Final instalment** | Final instalment amount, if it differs due to rounding |
| `installmentSchedule[].estimatedDueDate` | — | Estimated due date for each instalment, if a shipment date is used to generate the schedule |
#### Option B — Server-side calculation
Use the Ratepay XML API `CALCULATION_REQUEST` operation if you prefer to retrieve plan values from the server instead of calculating them client-side.
The response contains the core instalment values needed for display and checkout mapping.
See also: [CALCULATION_REQUEST](/docs/developer/api_integration/payment_1.8/payment_api_documentation/the_ratepay_gateway_operations/instalment_calculator/calculation_request)
For Pay in 3, both approaches can support the same buyer-facing disclosure values. Choose the option that best fits your architecture.
```xml CALCULATION_REQUEST.xml
MyTestsystem
CALCULATION_REQUEST
INTEGRATION_TE_DACH
4c0a11923fa3433fb168f9c7176429e9
200.00
3
```
```xml CALCULATION_RESPONSE.xml
MyTestsystem
CALCULATION_REQUEST
INSTALLMENT_PLAN
2026-03-29T13:37:08.000
Successfully
Calculation reason: FULFILLED_CONDITION: The payment plan fulfilled the conditions.
Calculation successful
200
200
0
0
0
0
0
3
66.67
66.66
2
```
#### Key response fields
For Pay in 3, you can ignore `` and `` because these values are always `0`.
| XML field | UI label | Meaning |
| --- | --- | --- |
| `` | **Per month** | Regular monthly instalment amount |
| `` | **Duration** | Number of monthly instalments |
| `` | **Effective Interest** | Effective interest rate shown to the buyer |
| `` | **Interest** | Total interest amount |
| `` | **Total** | Total amount payable |
| `` | **Final instalment** | Final instalment amount, if it differs due to rounding |
| `` | — | Day of month used for collection and for installment schedule logic |
### 2. Product page integration
On the product page, you may show a Pay in 3 teaser near the product price.
#### Recommended product page behavior
- calculate Pay in 3 plan details using **chosen calculation approach** (see [Calculation Approaches](/docs/developer/api_integration/payment_1.8/payment_api_documentation/how_to_integrate_ratepay/pay_in_3#1-choose-your-calculation-approach))
- show the **monthly instalment amount**
- show the **duration**
- provide a **Learn more** link or similar interaction
- open a modal or detail view with the full plan values
- update values dynamically when the product price changes, for example after variant selection
#### Required detail values for the modal or expanded view
| Label | Description |
| --- | --- |
| Per month | Monthly instalment amount |
| Duration | Number and duration of instalments |
| Effective Interest | Annual percentage rate |
| Interest | Total interest amount |
| Total | Total amount payable |
#### Example UI
| Product page teaser | Product page modal |
| --- | --- |
| | |
### 3. Cart page integration
The cart page follows the same principle as the product page, but the amount must always reflect the current cart total.
#### Recommended cart page behavior
- calculate Pay in 3 plan details using **chosen calculation approach** (see [Calculation Approaches](/docs/developer/api_integration/payment_1.8/payment_api_documentation/how_to_integrate_ratepay/pay_in_3#1-choose-your-calculation-approach))
- update the values whenever quantity or items change
- show the teaser in the cart summary area
- provide a “Learn more” interaction with the detailed values
#### Example UI
| Cart page teaser | Cart page modal |
| --- | --- |
| | |
### 4. Checkout integration
| Step | Purpose |
| --- | --- |
| `PAYMENT_INIT` | Initialize the Ratepay transaction and receive `transaction-id` |
| Plan calculation | Generate the display values and instalment details |
| Buyer-facing checkout display | Show the selected Pay in 3 plan and required inputs |
| `PAYMENT_REQUEST` | Send customer, basket, and payment data to Ratepay |
| `PAYMENT_CONFIRM` | Finalize the transaction if required for your setup |
#### Submit `PAYMENT_INIT`
The first step of the checkout workflow is `PAYMENT_INIT`. This operation initializes the transaction and returns a `transaction-id` that must be included in subsequent API calls.
See also: [PAYMENT_INIT](/docs/developer/api_integration/payment_1.8/payment_api_documentation/the_ratepay_gateway_operations/payment_init)
```xml PAYMENT_INIT_REQUEST.xml
myshop
PAYMENT_INIT
INTEGRATION_TE_DACH
4c0a11923fa3433fb168f9c7176429e9
```
```xml PAYMENT_INIT_RESPONSE.xml
MyTestsystem
xx-xxxxxxxxxxxxxx
3EQP.SBYM.CBSR.3302
PAYMENT_INIT
STATUS_RESPONSE
2026-01-06T16:56:19.000
Successfully
Processing successful
Transaction initialized
```
#### Calculate plan details for checkout
Use the final checkout basket amount to generate the Pay in 3 plan values using **chosen calculation approach**.
See [Calculation Approaches](/docs/developer/api_integration/payment_1.8/payment_api_documentation/how_to_integrate_ratepay/pay_in_3#1-choose-your-calculation-approach)
#### Display Pay in 3 in checkout
When the buyer selects Pay in 3 as the payment method, your checkout should render the Pay in 3 plan values and collect the required payment data.
Checkout UI for Pay in 3 should generally include
- credit term values for the selected plan
- instalment amount breakdown
- date of birth input
- Terms of Payment and Privacy / Risk Check links
near the order button.
For Pay in 3 with payments via direct debit, additionally these inputs are needed:
- IBAN input
- bank account owner input
- SEPA mandate text and consent checkbox
The legal requirements for what must be shown at checkout are defined on the [Pay in 3 legal requirements](/docs/legal/a_legal_requirements_for_the_integration/pay_in_3_integration) page.
By default, checkout should avoid fixed due dates because the actual schedule depends on shipment timing. A buyer-friendly simplified breakdown is acceptable, or you may show estimated due dates only if they are based on a reliable shipment date.
#### Example UI
| Checkout compact view | Checkout expanded view |
| --- | --- |
| | |
#### Map values into `PAYMENT_REQUEST`
After the buyer selects Pay in 3 and provides the required data, send `PAYMENT_REQUEST`.
#### Mapping from client-side calculation result
| `PAYMENT_REQUEST` field | Source from `calculatePayIn3()` |
| --- | --- |
| `` | `repaymentPlan.numberOfInstallments` |
| `` | `repaymentPlan.regularInstallmentAmount` |
| `` | `repaymentPlan.finalInstallmentAmount` |
| `` | `creditTerms.nominalInterestRatePercentage` |
| `` | Constant value `2` for direct debit, `28` for bank transfer |
| `` | `creditTerms.totalPayableAmount` |
#### Mapping from `CALCULATION_REQUEST` response
| `PAYMENT_REQUEST` field | Source from XML response |
| --- | --- |
| `` | `` |
| `` | `` |
| `` | `` |
| `` | `` |
| `` | `` |
| `` | `` |
#### Submit `PAYMENT_REQUEST`
`PAYMENT_REQUEST` is the main checkout operation. It sends the customer details, basket data, and selected Pay in 3 payment details to Ratepay.
See also: [PAYMENT_REQUEST](/docs/developer/api_integration/payment_1.8/payment_api_documentation/the_ratepay_gateway_operations/payment_request)
#### Main request areas
Pay in 3 via direct debit (recommended)
| Area | Examples |
| --- | --- |
| Customer data | Name, gender, date of birth, IP address, email, phone |
| Addresses | Billing and shipping addresses |
| Bank account | IBAN, BIC, account holder |
| Shopping basket | Items, discounts, shipping, total amount |
| Payment | Installment details and debit pay type |
```xml PAYMENT_REQUEST.xml
MyTestsystem
xx-xxxxxxxxxxxxxx
PAYMENT_REQUEST
INTEGRATION_TE_DACH
4c0a11923fa3433fb168f9c7176429e9
O-1234-HDZ
12345
xxxxxxxxxxxxxxxxxxxxxxxx
Max
Mustermann
M
1982-10-31
127.0.0.1
test@test.de
030123456
Nicht-Versenden-Strasse
1
12345
Testhausen
DE
Strasse der Lieferadresse
2
54321
Testhausen
DE
Max Mustermann
DE44100500001654698497
BELADEBEXXX
DE
yes
- Example Product
Versandkosten
200.00
3
66.67
66.66
0
2
DIRECT-DEBIT
```
```xml PAYMENT_REQUEST_RESPONSE.xml
MyTestsystem
xx-xxxxxxxxxxxxxx
PAYMENT_REQUEST
PAYMENT_PERMISSION
12345
2026-01-06T16:30:20.000
Successfully
Request successful
Transaction result pending
Nicht-Versenden-Strasse
1
12345
Testhausen
DE
200.00
3
66.67
66.66
0
2
DIRECT-DEBIT
DG0479414D0
```
Pay in 3 via bank transfer
| Area | Examples |
| --- | --- |
| Customer data | Name, gender, date of birth, IP address, email, phone |
| Addresses | Billing and shipping addresses |
| Shopping basket | Items, discounts, shipping, total amount |
| Payment | Installment details and debit pay type |
```xml PAYMENT_REQUEST.xml
MyTestsystem
xx-xxxxxxxxxxxxxx
PAYMENT_REQUEST
INTEGRATION_TE_DACH
4c0a11923fa3433fb168f9c7176429e9
O-1234-HDZ
12345
xxxxxxxxxxxxxxxxxxxxxxxx
Max
Mustermann
M
1982-10-31
127.0.0.1
test@test.de
030123456
Nicht-Versenden-Strasse
1
12345
Testhausen
DE
Strasse der Lieferadresse
2
54321
Testhausen
DE
DE
yes
- Example Product
Versandkosten
200.00
3
66.67
66.66
0
28
BANK-TRANSFER
```
```xml PAYMENT_REQUEST_RESPONSE.xml
MyTestsystem
xx-xxxxxxxxxxxxxx
PAYMENT_REQUEST
PAYMENT_PERMISSION
12345
2026-01-06T16:30:20.000
Successfully
Request successful
Transaction result pending
Nicht-Versenden-Strasse
1
12345
Testhausen
DE
200.00
3
66.67
66.66
0
28
BANK-TRANSFER
DG0479414D0
```
#### Submit `PAYMENT_CONFIRM`
After a successful `PAYMENT_REQUEST`, finalize the transaction with `PAYMENT_CONFIRM` if this step is required for your integration setup.
See also: [PAYMENT_CONFIRM](/docs/developer/api_integration/payment_1.8/payment_api_documentation/the_ratepay_gateway_operations/place_order/payment_confirmation)
The necessity of `PAYMENT_CONFIRM` depends on your integration setup. Please confirm with your Ratepay counterpart whether this step is required.
```xml PAYMENT_CONFIRM_REQUEST.xml
MyTestsystem
xx-xxxxxxxxxxxxxx
PAYMENT_CONFIRM
INTEGRATION_TE_DACH
4c0a11923fa3433fb168f9c7176429e9
O-1234-HDZ
```
```xml PAYMENT_CONFIRM_RESPONSE.xml
MyTestsystem
xx-xxxxxxxxxxxxxx
PAYMENT_CONFIRM
STATUS_RESPONSE
O-1234-HDZ
2026-04-09T17:57:07.000
Successfully
No RMS reason code
Transaction result successful
```
## Recommended implementation pattern
If you want a pragmatic setup with low complexity, use this pattern:
| Stage | Suggested implementation |
| --- | --- |
| Product page | Client-side calculation + teaser + “Learn more” modal |
| Cart page | Reuse the same function with cart total |
| Checkout display | Reuse the same function with final basket amount |
| Checkout API flow | `PAYMENT_INIT` → `PAYMENT_REQUEST` → `PAYMENT_CONFIRM` if required |
| Legal compliance | Keep the displayed fields aligned with the legal requirements page |
## Further resources
- [Pay in 3 legal requirements](/docs/legal/a_legal_requirements_for_the_integration/pay_in_3_integration)
- [CALCULATION_REQUEST](/docs/developer/api_integration/payment_1.8/payment_api_documentation/the_ratepay_gateway_operations/instalment_calculator/calculation_request)
- [PAYMENT_INIT](/docs/developer/api_integration/payment_1.8/payment_api_documentation/the_ratepay_gateway_operations/payment_init)
- [PAYMENT_REQUEST](/docs/developer/api_integration/payment_1.8/payment_api_documentation/the_ratepay_gateway_operations/payment_request)
- [PAYMENT_CONFIRM](/docs/developer/api_integration/payment_1.8/payment_api_documentation/the_ratepay_gateway_operations/place_order/payment_confirmation)