> ## Documentation Index
> Fetch the complete documentation index at: https://dokumentation.websale.de/llms.txt
> Use this file to discover all available pages before exploring further.

# Prices, taxes, and currency

> How VAT is determined, how prices are formatted and displayed in alternative currencies, and how vouchers, bonus points, subsidies, promotional prices, and climate-neutral production affect the totals.

The final amount of an order is built up in several layers. At the bottom is the question of whether prices are net or gross prices and which VAT rate applies to which product and which delivery country. Above that is the presentation, meaning decimal places, separators, and currency. At the top are the amounts that subtract or add something, from vouchers and bonus points to climate-neutral production.

This page follows the same layers. First VAT, then currency and price formatting, then deductions and surcharges.

Reference: Shop configuration shop.config, overview of all sections

## `VAT`

Defines how VAT is handled. This is the central section for all tax matters in the shop.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<VAT>
   ProductPrices                                = gross
   Name-Txt                                     = Mehrwertsteuer
   NameGross-Txt                                = enthaltene Mehrwertsteuer
   NameNet-Txt                                  = zuzüglich Mehrwertsteuer
   WithoutVAT-Txt                               = Mehrwertsteuerfreie Bestellung
   Discount-Txt                                 = abzüglich deutscher MwSt.
   DefaultVATRate                               = 1
   TaxCountries                                 = DEU,AUT,BEL
   TaxIDCountries                               = BEL,BGR,DNK
   VATRateDefinition-Mode                       = ByDelivCountry-Managed
   VATRateDefinition-ByDelivCountry-StandardISO = DEU
</VAT>
```

### Net or gross

| Parameter | Purpose | Values |
| - | - | - |
| `ProductPrices` | whether the stored prices are net or gross prices | `net`, `gross`. Default: `gross` |

> [!NOTE]
>
> A price type stored in the customer account **overrides** this setting for the logged-in buyer. This lets a shop be a net shop for business customers and a gross shop for private customers. This is not described in the previous documentation.

### Texts in the order data

| Parameter | Appears |
| - | - |
| `Name-Txt` | as the general label for VAT |
| `NameGross-Txt` | instead of `Name-Txt` if the shop is a gross shop |
| `NameNet-Txt` | instead of `Name-Txt` if the shop is a net shop |
| `Discount-Txt` | when VAT is deducted from gross prices |
| `WithoutVAT-Txt` | when no VAT was calculated for the order |

> [!WARNING]
>
> At least one of the three name texts must be filled in. If `NameGross-Txt` and `NameNet-Txt` are empty, `Name-Txt` must be filled in. Otherwise **no VAT calculation at all** takes place.

### VAT index for costs

Products don't carry a fixed tax rate but an index between 1 and 10. For everything that isn't a product, this section defines which index applies and whether it's fixed or derived from the basket.

| Parameter | Applies to | Values |
| - | - | - |
| `DefaultVATRate` | other costs, for example payment costs | 1 to 10. Default: 1 |
| `DeliveryVATRate` | shipping costs, only with `DeliveryVATType = static` | 1 to 10. Default: 1 |
| `DeliveryVATType` | determination for shipping costs | `dynamic`, `static`. Default: `dynamic` |
| `PaymentCostVATType` | determination for payment costs | `dynamic`, `static`. Default: `static` |
| `SurchargeVATType` | determination for surcharges | `dynamic`, `static`. Default: `static` |
| `SampleCopyVATRate` | specimen copies, only with `SampleCopyVATType = static` | 1 to 10. Default: 1 |
| `SampleCopyVATType` | determination for specimen copies | `dynamic`, `static`. Default: `static` |
| `MultiDeliveryAddressUsageVATRate` | shipping costs for split orders, only with `static` | 1 to 10. Default: 1 |
| `MultiDeliveryAddressUsageVATType` | determination for shipping costs for split orders | `dynamic`, `static`. Default: `static` |

`static` means that the specified index is used. `dynamic` means that the VAT rate with the highest total in the basket determines the rate.

> [!WARNING]
>
> With `dynamic`, the associated index parameter must **not contain a value**.

> [!NOTE]
>
> Two peculiarities of the evaluation are not documented and are added here.
>
> If `DefaultVATRate` is missing entirely, the kernel uses the value from `DeliveryVATRate` as the default index. An empty `DefaultVATRate`, on the other hand, results in 1.
>
> An index above the maximum is silently set to the maximum, and the value 0 becomes 1. No error message appears.

Gift wrapping, bonus points, vouchers, subsidies, and climate-neutral production have their own `VATType` parameters in their respective sections.

### Country-dependent tax liability

| Parameter | Purpose | Values |
| - | - | - |
| `TaxCountries` | countries that are subject to VAT | comma-separated 3-letter ISO codes |
| `TaxIDCountries` | countries in which business customers with a valid VAT ID are exempt from tax | comma-separated 3-letter ISO codes |
| `TaxFreeIgnoreBillAddr` | use only the delivery country for the determination | `yes`, `no`. Default: `no` |

As a rule, these are all countries of the EU territory in each case.

> [!WARNING]
>
> If `TaxCountries` is missing or empty, **no** country-dependent tax collection takes place. VAT is then charged for all countries.
>
> If `TaxIDCountries` is missing or empty, **no** tax exemption via the VAT ID takes place.

The former parameter name `Countries` is still read as a fallback if `TaxCountries` is missing or empty.

#### Tax exemption via the delivery address

Normally, the VAT ID of the billing address determines the exemption. The following two parameters shift this to the delivery address.

| Parameter | Purpose | Values |
| - | - | - |
| `TaxFree4TaxID-DeliveryDependent` | make the exemption dependent on the VAT ID of the delivery address | `yes`, `no`, `test`. Default: `no` |
| `TaxFree4TaxID-DelivAddrTaxIDField` | suffix field in which the VAT ID of the delivery address is maintained | for example `Suffix45` |

The value `test` only takes effect in the shop's general test mode. In production, the VAT ID of the billing address is then used.

### Source of the tax rates

| Parameter | Purpose | Values |
| - | - | - |
| `VATRateDefinition-Mode` | where the tax rates come from | `Standard`, `ByDelivCountry-Managed`, `ByDelivCountry-Individual`. Default: `Standard` |
| `VATRateDefinition-TestMode` | different method in the general test mode | the same values. Default: same as `VATRateDefinition-Mode` |
| `VATRateDefinition-ByDelivCountry-StandardISO` | default delivery country as long as none is known yet | 3-letter ISO code |
| `VATRateDefinition-ByDelivCountry-ISOForVATDiscount` | ISO code of the company headquarters for the tax deduction on foreign deliveries | 3-letter ISO code |
| `VATRateDefinition-ByDelivCountry-DefinitionFile` | custom tax rate file | file name, for example `vatrates.config` |
| `VATRateDefinition-ByDelivCountry-TestDefinitionFile` | custom tax rate file for test mode | file name |

`Standard` reads the tax rates from the `VATRates` sections of this `shop.config`. `ByDelivCountry-Managed` takes them from the global WEBSALE VAT rate file. `ByDelivCountry-Individual` takes them from a file of your own, which must then be specified in `VATRateDefinition-ByDelivCountry-DefinitionFile`.

> [!NOTE]
>
> `VATRateDefinition-TestMode` only takes effect if the shop is actually running in test mode. In this case, the file from `VATRateDefinition-ByDelivCountry-TestDefinitionFile` is also used instead of the production file.
>
> If `VATRateDefinition-ByDelivCountry-ISOForVATDiscount` is empty, the kernel uses the value from `VATRateDefinition-ByDelivCountry-StandardISO`. This is not documented and is added here.

### Undocumented parameters

The kernel reads additional parameters in this section that are not described in the previous documentation.

| Parameter | Purpose | Values |
| - | - | - |
| `VATDetermination-Mode` | alternative method of tax determination | `PerPosition-ConsiderCountryOfProduction`, `TaxCase` |
| `VATDetermination-TestMode` | the same, effective only in the general test mode | the same values |
| `TaxCase-DefaultCountryOfProduction` | default country of production for tax case determination | 3-letter ISO code |
| `RegisteredCountry-List` | countries in which the merchant is registered for tax purposes | comma-separated ISO codes |
| `AllowedTaxIDCountry-List` | countries from which VAT IDs are accepted | comma-separated ISO codes |

> [!WARNING]
>
> These parameters affect the tax calculation. Only set them after consulting WEBSALE AG. The release status is still to be clarified.

## `VATRates`

Assigns a specific rate to each VAT index. Products carry only the index, not the rate. If the statutory VAT changes, you only need to adjust this one section instead of every single product.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<VATRates>
   1 = 19
   2 = 7
   3 = 7.7
</VATRates>
```

| Parameter | Purpose | Values |
| - | - | - |
| `1` to `10` | VAT rate for this index, without a percent sign | integer or decimal number with a period, for example `19` or `7.7` |

> [!NOTE]
>
> This method is deprecated but still supported. WEBSALE instead recommends the global WEBSALE VAT rate file or a custom file, both via `VATRateDefinition-Mode` in the `VAT` section.

> [!WARNING]
>
> The `VATRates` sections are **only** read if `VATRateDefinition-Mode` is set to `Standard`. In the other two modes they have no effect.

In addition to `VATRates`, you can use either country-dependent **or** date-dependent sections, never both and no combination of them. For a time-controlled changeover in specific countries, use campaign control.

### Country-dependent tax rates

For each delivery country, you can create a separate section whose name ends with the 3-letter ISO code.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<VATRates-AUT>
   1 = 20
   2 = 10
</VATRates-AUT>

<VATRates-CHE>
   1 = 7.7
   2 = 2.5
</VATRates-CHE>
```

When the buyer selects a delivery country, the shop first looks for the matching country-dependent section. If it's found, its values apply. If it isn't found, the values from `VATRates` apply.

> [!NOTE]
>
> The `VATRates` section without a suffix is **always** required. Countries without different rates don't need their own section.
>
> Which delivery country applies depends on the order path. In the normal ordering process, it's the delivery country selected by the buyer. With PayPal Express Checkout, Klarna Express Checkout, and PayPal Checkout, the respective service provides the delivery country. This is not documented and is added here.

### Date-dependent tax rates

For a cutoff date, you can create a section whose name ends with the date in the format `JJJJMMTT` (YYYYMMDD). The number represents the **last** day on which these rates apply.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<VATRates>             # gilt ab dem 01.01.2027
   1 = 19
   2 = 7
</VATRates>
<VATRates20261231>     # gilt vom 01.07.2026 bis 31.12.2026
   1 = 16
   2 = 5
</VATRates20261231>
<VATRates20260630>     # gilt bis zum 30.06.2026
   1 = 19
   2 = 7
</VATRates20260630>
```

> [!WARNING]
>
> The order matters, similar to overriding a CSS class.
>
> The `VATRates` section without a date must **always come first** and contain the rates that apply again after all cutoff dates.
>
> The dated sections follow, with the latest cutoff date first.

## `Currency-Txt`

Defines the base currency of the shop. This is the currency in which the product prices are stored.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<Currency-Txt>
   Name-!                = EUR
   Symbol                = &euro;
   PaymetricCurrencyCode =
</Currency-Txt>
```

| Parameter | Purpose | Values |
| - | - | - |
| `Name-!` | base currency as a 3-letter ISO code | for example `EUR`, `CHF`, `USD`. Default: `EUR` |
| `Symbol` | HTML encoding of the currency symbol | for example `&euro;`, `&pound;`, `&yen;` |
| `PaymetricCurrencyCode` | currency code for Paymetric | numeric according to ISO 4217 |

## `AlternativeCurrency`

Additionally displays prices in other currencies. WEBSALE AG keeps the exchange rates up to date unless you provide your own file.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<AlternativeCurrency>
   Allow                     = yes
   GeoIP-Allow               = no
   SelectBoxMode             = Txt
   SelectBoxOnchange         = submit();
   NoAlternativeCurrency-Txt = Keine alternative Währung anzeigen
   <+Entry>
      ISO    = USD
      Txt    = US-Dollar
      Symbol = $
   </+Entry>
   <+Entry>
      ISO    = GBP
      Txt    = Britische Pfund
      Symbol = &pound;
   </+Entry>
</AlternativeCurrency>
```

| Parameter | Purpose | Values |
| - | - | - |
| `Allow` | enable alternative currency display | `yes`, `no` |
| `GeoIP-Allow` | automatically determine the alternative currency via GeoIP at shop start | `yes`, `no` |
| `SelectBoxMode` | which field appears in the automatically generated selection list | `Txt`, `Symbol`, `ISO` |
| `SelectBoxOnchange` | passed to the selection list as the `onchange` attribute | JavaScript |
| `NoAlternativeCurrency-Txt` | text for the "don't show a currency" entry | text |
| `CurrencyConversionFile` | your own file with exchange rates, relative to the configuration directory | file name |

> [!NOTE]
>
> The values of `SelectBoxMode` correspond to the parameter names of the `+Entry` sections. In the source documentation, the first value was listed as `Text` instead of `Txt`. This is corrected here so that parameter and value match.

One `+Entry` subsection per offered currency.

| Parameter | Purpose | Values |
| - | - | - |
| `ISO` | 3-letter ISO code of the currency | for example `USD` |
| `Txt` | optional display text | text. Default: the ISO code |
| `Symbol` | optional currency symbol | text. Default: the ISO code |

## `PriceFormat`

Defines how prices are displayed. All settings are optional.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<PriceFormat>
   DecimalPlaces      = 2
   DecimalSeparator   = ,
   ThousandSeparator  = .
   ZeroIntegralPart   =
   ZeroFractionalPart = --
   JoinCurrency       = ~WS-Price~ ~WS-Currency~
   JoinCurrencySymbol = ~WS-Price~ ~WS-CurrencySymbol~
</PriceFormat>
```

| Parameter | Purpose | Values |
| - | - | - |
| `DecimalPlaces` | number of displayed decimal places | 0 to 2. Default: 2 |
| `DecimalSeparator` | decimal separator | any character. Default: period |
| `ThousandSeparator` | thousands separator | any character. Default: none |
| `ZeroIntegralPart` | display of a 0 before the decimal point | `hide` or a replacement character |
| `ZeroFractionalPart` | display of 00 after the decimal point | `hide` or a replacement character |
| `JoinCurrency` | position of price and ISO currency code relative to each other | Default: `~WS-Price~ ~WS-Currency~` |
| `JoinCurrencySymbol` | position of price and currency symbol relative to each other | Default: `~WS-Price~ ~WS-CurrencySymbol~` |

> [!NOTE]
>
> `JoinCurrency` and `JoinCurrencySymbol` were missing from the sample code in the source documentation, although they appear in the parameter table. They are added here.

`ZeroIntegralPart` only affects the `PR-IPart()` tag, and `ZeroFractionalPart` only affects `PR-FPart()`. With `ZeroIntegralPart = -`, 0,95 becomes the output `-,95`. With `ZeroFractionalPart = --`, 2,00 becomes the output `2,--`.

The two join parameters only affect elements that WEBSALE V8s generates automatically, for example the order data format. If you positioned the currency manually in templates, you also need to adjust those templates.

> [!WARNING]
>
> The shop passes prices to technical systems unformatted, but only to systems it knows. External systems embedded in templates, for example via tracking pixels or JavaScript, receive the tags `BASKET-ValueOfGoods`, `BASKET-OrderTotal`, and `BASKET-SubTotalNet` in your configured formatting.
>
> Check in advance whether these systems correctly read your decimal and thousands separators. If not, you need to stick with the defaults in the shop, meaning a period as the decimal separator and no thousands separator.

## `Voucher-Txt`

Enables voucher redemption and holds the related texts.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<Voucher-Txt>
   Name                                          = Gutschein
   VoucherInputLocation                          = Basket
   NewCustomerDefinition                         = NoCustomerNumber
   MultiVoucher-Allow                            = no
   VATType                                       = static
   UsedVoucherNumber                             = (verwendete Gutscheinnummer: ~VoucherNumber~)
   Remainder2                                    = Verfügbarer Restbetrag für Ihre nächste Bestellung:
   VoucherNotApplicable                          = Der Gutschein ist mit diesen Produkten nicht verwendbar.
</Voucher-Txt>
```

### Basic settings

| Parameter | Purpose | Values |
| - | - | - |
| `Name` | label for identification | text, for example `Gutschein` |
| `VoucherInputLocation` | on which templates the voucher input appears | see below |
| `NewCustomerDefinition` | who counts as a new customer for a new-customer voucher | `NoOrder`, `NoCustomerNumber`. Default: `NoCustomerNumber` |
| `ExternalVouchers-Allow` | allow import of externally generated voucher data | `yes`, `no`. Default: `no` |
| `VATType` | determination of the VAT rate for vouchers | `dynamic`, `static`. Default: `static` |

`NewCustomerDefinition = NoOrder` allows the voucher as long as the buyer hasn't completed an order yet, even if a customer number already exists. `NoCustomerNumber` requires that no customer number exists yet.

> [!NOTE]
>
> `VoucherInputLocation` is a **comma-separated list**, not a single value. Allowed values are `Basket`, `PlaceOrder`, and `PayDeliv`. The value `PlaceOrderAndBasket` still exists for compatibility reasons and corresponds to `Basket,PlaceOrder`. If no entry matches, `Basket` is set.
>
> The list capability and the value `PayDeliv` for the payment and shipping method page are not described in the previous documentation and are added here.

### Multiple vouchers

| Parameter | Purpose | Values |
| - | - | - |
| `MultiVoucher-Allow` | redeem multiple vouchers at the same time | `yes`, `no`, `test`. Default: `no` |
| `MultiVoucher-MinOrderBehavior` | calculation of the minimum order value with multiple vouchers | `UseSum`, `UseMax`. Default: `UseSum` |
| `UseRoundedPercentalMultiVoucherDiscount` | use rounded intermediate results with multiple percentage vouchers | `yes`, `no`. Default: `no` |

With two vouchers with minimum order values of 20 and 30 euros, `UseSum` requires a basket over 50 euros, while `UseMax` only requires one over 30 euros.

`UseRoundedPercentalMultiVoucherDiscount = yes` rounds each voucher individually and then adds them up. With `no`, the values are added first and then rounded.

> [!NOTE]
>
> `MultiVoucher-MinOrderBehavior` was missing from the sample code in the source documentation, although the parameter appears in the table. It is added here.
>
> The value `test` only takes effect in the shop's general test mode. In addition, a change from `yes` to `no` only takes effect if no second voucher has been entered in the current session yet. This is not documented and is added here.

### META vouchers

A META voucher is a collective code that combines several individual voucher codes. When the buyer enters the META code, all associated sub-vouchers are redeemed. Only one META voucher can be redeemed at a time.

| Parameter | Purpose | Values |
| - | - | - |
| `MetaVoucherErrorMode` | behavior when a sub-voucher is invalid | `reject-all`, `accept-valid`. Default: `reject-all` |
| `MetaVoucherShowAsBlock` | remove all associated sub-vouchers when one is removed | `yes`, `no`. Default: `yes` |

### Applicability to the basket

| Parameter | Purpose | Values |
| - | - | - |
| `PreventOrdersWithVoucherNotApplicable` | prevent order completion if the voucher isn't applicable | `yes`, `no`. Default: `no` |
| `ShowPercentalVoucherDiscountForPosition-Allow` | calculate a percentage voucher at the line item level | `yes`, `no`. Default: `no` |
| `SpecialSetting1-Allow` | apply a promotional voucher to the additional costs if no product eligible for promotional vouchers is in the basket | `yes`, `no`. Default: `no` |
| `UnresolvedVoucherProducts-Allow` | allow a base product with variants as a voucher product | `yes`, `no`. Default: `no` |
| `AllowToRedeemVoucherWithoutMatchingCatProdFilter` | allow redemption even without a matching category or product filter | `yes`, `no`. Default: `no` |

> [!WARNING]
>
> In the kernel, the parameter is named `SpecialSetting1-Allow`, meaning **Setting in the singular**. The previous documentation wrote `SpecialSettings1-Allow` with an s. Under the wrong name, the parameter has no effect and no message appears. This is corrected here.

> [!NOTE]
>
> `AllowToRedeemVoucherWithoutMatchingCatProdFilter` is not described in the previous documentation and is added here.

With `UnresolvedVoucherProducts-Allow = yes`, a base product without a preselected variant can be stored as a voucher product. The buyer then selects the variant in the basket.

### Texts

| Parameter | Appears |
| - | - |
| `Remainder2` | as a note about the remaining amount available for the next order |
| `UsedVoucherNumber` | in the readable order format after the voucher amount used. The local tag `VoucherNumber` is replaced by the number |
| `VoucherNotApplicable` | on the basket page when a promotional voucher is applied to a basket without products eligible for promotional vouchers |

## `BonusPoints`

Configures the merchant's own bonus points system.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<BonusPoints>
   Allow                    = yes
   AmountPerPoint           = 0.5
   MinInputPoints           = 0
   MinOrderValue            = 50.00
   VATType                  = static
   CashedPoints-Txt         = abzüglich ~WS-CashedBonusPoints~ Bonuspunkte
   OrderPoints-Txt          = Bonuspunkte der Bestellung
   PointsAccountBalance-Txt = Kontostand Bonuspunkte
</BonusPoints>
```

| Parameter | Purpose | Values |
| - | - | - |
| `Allow` | enable the bonus points system | `yes`, `no`. Default: `no` |
| `AmountPerPoint` | value of one point in the shop currency | number, for example `0.5` |
| `MinInputPoints` | minimum number of points from which redemption is possible | number |
| `MinOrderValue` | optional minimum order value for redemption | number, for example `50.00` |
| `VATType` | determination of the VAT rate for bonus points | `dynamic`, `static`. Default: `static` |
| `PriceTypeNet-UseBonusPointsOnTotalNetSum` | in net shops, deduct the points from the net total | `yes`, `no`. Default: `no` |

> [!NOTE]
>
> An `AmountPerPoint` of 0 or a missing value silently results in 1. This is not documented and is added here.

### Texts in the order data

| Parameter | Appears |
| - | - |
| `CashedPoints-Txt` | for bonus points redeemed for a credit |
| `OrderPoints-Txt` | for the points earned with this order |
| `PointsAccountBalance-Txt` | for the account balance |
| `BonusProductPointsPosition-Txt` | for the reward points of a line item |
| `BonusProductPointsTotal-Txt` | for the total points used for all reward products |
| `NotEnoughPoints-Txt` | when the buyer wants to order a reward product without a sufficient balance |

> [!WARNING]
>
> `PriceTypeNet-UseBonusPointsOnTotalNetSum` also reduces the VAT assessment basis by the redeemed amount. Note two restrictions.
>
> Products can then no longer be excluded from redemption, and the corresponding product property is ignored.
>
> If products with different tax rates are in the basket and the redeemed amount is smaller than the net total, the assessment basis is reduced in ascending order starting from the first tax index.

## `CustomerSubvention`

Lets you grant each customer a subsidy.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<CustomerSubvention>
   Allow         = yes
   Name-Txt      = Zuschuss
   Remainder-Txt = Verfügbarer Zuschuss für die nächste Bestellung
   VATType       = static
</CustomerSubvention>
```

| Parameter | Purpose | Values |
| - | - | - |
| `Allow` | grant subsidies in the shop | `yes`, `no`. Default: `no` |
| `Name-Txt` | text for the order data | text |
| `Remainder-Txt` | text for the subsidy still available for the next order | text |
| `VATType` | determination of the VAT rate for subsidies | `dynamic`, `static`. Default: `static` |

## `BuyXPayY`

Defines a promotion following the pattern "buy 3, pay for 2". WEBSALE V8s currently supports only one promotion, so the `+Entry` subsection may only occur once.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<BuyXPayY>
   <+Entry>
      Allow      = yes
      ID         = nimm3zahl2
      BuyX       = 3
      PayY       = 2
      Descr      = Nimm 3, zahl 2
      ValidFrom  = 01.11.2026
      ValidUntil = 31.12.2026
   </+Entry>
</BuyXPayY>
```

| Parameter | Purpose | Values |
| - | - | - |
| `Allow` | enable the promotion | `yes`, `no`. Default: `no` |
| `ID` | unique identifier, assigned to the product | alphanumeric |
| `BuyX` | minimum number of products purchased | number |
| `PayY` | number of products paid for | number |
| `Descr` | short description for display in the template | text, maximum 256 characters |
| `ValidFrom` | start date. If not specified, the promotion starts immediately | `TT.MM.JJJJ` (DD.MM.YYYY) |
| `ValidUntil` | end date. If not specified, the promotion runs indefinitely | `TT.MM.JJJJ` (DD.MM.YYYY) |

> [!WARNING]
>
> `BuyX` must be greater than `PayY`, and `PayY` must be greater than 0. If either condition is violated, the entire entry is silently discarded.
>
> The same applies to an entry without `Allow = yes` or without `ID`.
>
> The two dates are only evaluated if they are **exactly ten characters** long. A notation such as `1.11.2026` is ignored, and the promotion then applies without this limit.
>
> In the source documentation, the description of `PayY` referred to a parameter `BuyY`. `BuyX` is meant. This is corrected here.

## `ClimateNeutral`

Configures the climate-neutral production service via ClimatePartner.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<ClimateNeutral>
   CO2Factor                 = 0.0017242
   MinCO2CompensationAmount  = 0.001
   VATType                   = static
   VATRate-Value             = 1
   CertificateRetrieval-Mode = PerOrder
   CostAddition-List         = (0:2)(40:5%)(500:0)
   NotificationAddress       = admin@mein-shop.de
   LanguageCode              = de
</ClimateNeutral>
```

### Calculation

| Parameter | Purpose | Values |
| - | - | - |
| `CO2Factor` | **Required parameter**. Factor for calculating the emission quantity from the weight of all climate-neutral products in the basket | number, for example `0.0017242` |
| `MinCO2CompensationAmount` | minimum emission quantity from which climate-neutral production is offered | number, for example `0.001` |
| `ClimatePartnerMinCost-Value` | minimum costs for climate-neutral production | number, for example `0.01` |
| `CostAddition-List` | optional surcharge on the certificate costs | tiers, see below |

You get the value for `CO2Factor` from ClimatePartner.

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
# ab 0 EUR Zertifikatskosten: 2,00 EUR Aufschlag
# ab 40 EUR Zertifikatskosten: 5 Prozent Aufschlag
# ab 500 EUR Zertifikatskosten: kein Aufschlag
CostAddition-List = (0:2)(40:5%)(500:0)
```

The tiers are based on the **certificate costs** of the external provider, not on the value of goods. Certificate costs plus surcharge make up the total costs. All amounts are net prices. Up to 50 tiers are supported.

> [!NOTE]
>
> In the source documentation, the format specification of this parameter was labeled `(Warenwert:Kosten)` (value of goods:costs), while the explanatory text refers to certificate costs. This page consistently refers to certificate costs.

### VAT

| Parameter | Purpose | Values |
| - | - | - |
| `VATType` | determination of the VAT rate | `dynamic`, `static`. Default: `static` |
| `VATRate-Value` | VAT index, only with `VATType = static` | 1 to 10 |

> [!WARNING]
>
> If the VAT can't be calculated validly, climate-neutral production isn't offered at all.

### Certificate

| Parameter | Purpose | Values |
| - | - | - |
| `CertificateClient` | name in which the climate protection certificate is issued | tags of the billing address |
| `CertificateOrderDescr` | project name on the certificate | text with local tags |
| `CertificateRetrieval-Mode` | certificate per order or per basket line item | `PerOrder`, `PerBasketPosition`. Default: `PerOrder` |
| `CertificateRetrieval-TestMode` | the same in the shop's test mode | `PerOrder`, `PerBasketPosition`. Default: `PerOrder` |
| `ClimateNeutral-Txt` | optional description for the order data and the order history | text |
| `LanguageCode` | language code for requests to ClimatePartner | 2 letters, for example `de` |
| `NotificationAddress` | recipient of the error message if the certificate order fails | email address |

In `CertificateClient`, you can use all tags of the billing address, for example like this:

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
CertificateClient = {A-Company}~A-Company~{/A-Company}{!A-Company}~A-Salutation~ ~A-FirstName~ ~A-LastName~{/!A-Company}
```

In `CertificateOrderDescr`, the local tag `OrderNumber` is available. With `CertificateRetrieval-Mode = PerBasketPosition`, `ProductNumber` and `ProductName` are also available.

> [!WARNING]
>
> If ordering the certificate from ClimatePartner fails, the order in the shop is already completed and the certificate has been paid for. Manual follow-up with ClimatePartner is then required. For this reason, make sure to specify a `NotificationAddress`.

### Display of the emission quantity

| Parameter | Purpose | Values |
| - | - | - |
| `DecimalPlaces` | decimal places for the display in kg CO2 | number. Default: 2 |
| `DecimalSeparator` | decimal separator for this display | single character. Default: comma |
| `ThousandSeparator` | thousands separator for this display | single character. Default: period |

## BestPrice order module

The BestPrice order module is spread across four sections. One for the templates, one for the messages, and two for emails.

### `BestPrice`

```ini theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
<BestPrice>
   Template           = incl_bestprice.htm
   TemplateShowOrders = incl_bestprice_show_orders.htm
   PreorderMail       = mail_bestprice_preorder.htm
   PaymentCode        = 6
</BestPrice>
```

| Parameter | Purpose |
| - | - |
| `Template` | template of the module |
| `TemplateShowOrders` | template of the reservation overview |
| `PreorderMail` | email template for the preorder confirmation |
| `PaymentCode` | code of the invoice payment method |

> [!WARNING]
>
> `PaymentCode` must be set to `6` and must not be changed.

### `BestPrice-Txt`

The module's messages.

| Parameter | Appears when |
| - | - |
| `ErrorNoAmount` | no quantity was selected |
| `ErrorNoAddress` | no address has been entered for the account yet |
| `ErrorNotLoggedIn` | the buyer isn't logged in |
| `ErrorCampaignNotActivated` | the campaign isn't active yet |
| `ErrorCampaignFinished` | the campaign has already ended |
| `ErrorNotConfirmed` | the terms and conditions haven't been accepted yet |
| `ErrorNoDoubleOrder` | a reservation is submitted more than once |
| `InfoPreordered` | the reservation was successful |

### `BestPrice-PreorderMail`

Confirmation email to the buyer.

| Parameter | Purpose | Values |
| - | - | - |
| `Allow` | enable the confirmation email | `yes`, `no`. Default: `no` |
| `Subject-Txt` | subject | text |
| `SenderName` | name of the sender | text |
| `SenderAddress` | email address of the sender | email address |

> [!NOTE]
>
> In the source documentation, `SenderAddress` was described as the "email address of the recipient". It is the sender address. This is corrected here. The recipient is the buyer and is determined from the order.

### `BestPrice-OrderErrorMail`

Email to the shop administrator if the final order generation fails.

| Parameter | Purpose | Values |
| - | - | - |
| `Allow` | enable the notification | `yes`, `no`. Default: `no` |
| `Subject-Txt` | subject | text |
| `SenderName` | name of the sender | text |
| `SenderAddress` | email address of the sender | email address |
| `ReceiverAddress` | email address of the recipient | email address |


## Related topics

- [Configuration currency.dat](https://dokumentation.websale.de/en/v8s/referenz/konfigurationen/konfiguration-currency-dat.md)
- [finance - Currencies & taxes](https://dokumentation.websale.de/en/konfiguration/finance-wahrungen-steuern.md)
- [Prices and costs](https://dokumentation.websale.de/en/v8s/preise/preise-und-kosten.md)
- [Configuration deep links](https://dokumentation.websale.de/en/admin-interface/konfigurations-deeplinks.md)
- [Shopping cart, prices, currency and bonus](https://dokumentation.websale.de/en/v8s/referenz/tags/ws/warenkorb-preise-waehrung-und-bonus.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.