> ## 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.

# Practical examples - Vouchers

> Practical examples for vouchers in WEBSALE: input form with maximumCount check, validation, redemption in checkout as well as download links for purchased vouchers.

This section contains practical examples for using vouchers in the template. The first examples cover redemption in the checkout, the last one covers outputting purchased vouchers.

Vouchers are created in the Admin Interface. This is described on the pages [Vouchers](/en/admin-interface/marketing/gutscheine) and [Creating a purchase voucher product](/en/admin-interface/katalog/produkte/kaufgutschein-produkt).

***

## Voucher input form with `maximumCount` check

The input form is only displayed as long as fewer vouchers have been redeemed than allowed. As soon as the upper limit is reached, the form disappears automatically.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $cActionVoucherAdd = $wsActions.create("VoucherAdd") }}
{{ include "components/errorAlert.htm" with $cAction = $cActionVoucherAdd, $cViewEachField = true }}

{{ if len($wsVoucher.vouchers) < $wsVoucher.maximumCount }}
    <form method="post" action="{{= $wsViews.current.url() }}" data-ws-ajax-form>
        <input type="hidden" name="wsReplaceIds" value="wsBasketWrapper,wsBasketEntries,wsBasketOffcanvasContent">
        <input type="hidden" name="wsact"    value="{{= $cActionVoucherAdd.id }}">
        <input type="hidden" name="wscsrf"   value="{{= $cActionVoucherAdd.csrf }}">
        <input type="hidden" name="wstarget" value="{{= $wsViews.current.url() }}">

        <input type="text" name="id" value="" placeholder="Enter voucher code">
        <button type="submit">Redeem</button>
    </form>
{{ /if }}
```

<Info>
  Error messages (e.g. "Minimum order value not reached") are defined and rendered via `components/errorAlert.htm`.
</Info>

***

## Displaying the list of redeemed vouchers

A separate small form with a unique ID is output for each voucher. Valid vouchers appear in green, invalid ones in red.

```text theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ if $wsVoucher.vouchers }}
    <p>Redeemed vouchers</p>

    {{ foreach $cVoucher in $wsVoucher.vouchers }}
        {{ var $cVoucherIsValid = $cVoucher.valid | ifNull(true) }}
        {{ var $cActionVoucherDelete = $wsActions.create("VoucherDelete") }}

        <form method="post"
              action="{{= $wsViews.viewUrl('basket.htm') }}"
              data-ws-ajax-form>
            <input type="hidden" name="wsReplaceIds" value="wsBasketWrapper,wsBasketEntries,wsBasketOffcanvasContent">
            <input type="hidden" name="id"       value="{{= $cVoucher.id }}">
            <input type="hidden" name="wsact"    value="{{= $cActionVoucherDelete.id }}">
            <input type="hidden" name="wscsrf"   value="{{= $cActionVoucherDelete.csrf }}">
            <input type="hidden" name="wstarget" value="{{= $wsViews.current.url() }}">

            <span>{{= $cVoucher.id }}</span>
            <button type="submit">Remove</button>
        </form>
    {{ /foreach }}
{{ /if }}
```

***

## Displaying all redeemed vouchers in the basket

In this example, all redeemed vouchers are shown in the basket. The customer can transparently see which codes are in the system and which one is currently being applied. Vouchers that have no effect are not added to the list and are indicated via an error message.

```text theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $cVoucherCount = 0 }}
{{ if $wsVoucher.vouchers }}
    {{ foreach $cVoucher in $wsVoucher.vouchers }}
        {{ $cVoucherCount = $cVoucherCount + 1 }}
        <tr>
            <td>
                <div>{{ if $cVoucherCount == 1 }}Voucher{{ else }}Additional voucher{{ /if }}</div>
                <div>{{= $cVoucher.id }}</div>
            </td>
            <td>
                -{{= $cVoucher.value | currency }}
            </td>
        </tr>
    {{ /foreach }}
{{ /if }}
```

***

## Output voucher errors with reason and voucher ID

Instead of a generic notice, the customer sees the specific reason per voucher why the voucher does not apply. The texts come from [`checkout.voucherErrors`](/en/konfiguration/checkout-bestellablauf#checkout-vouchererrors-fehlertexte-zu-wirkungslosen-gutscheinen); the fallback to `code` only kicks in if no text is maintained.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ if $wsCheckout.isOrderBlockedByIneffectiveVoucher }}
    <div class="alert alert-danger" role="alert">
        <strong>Gutscheine können nicht eingelöst werden:</strong>
        <ul>
            {{ foreach $cError in $wsCheckout.ineffectiveVoucherErrors }}
                {{ if $cError.details.voucherId }}
                    <li>
                        Gutschein <strong>{{= $cError.details.voucherId }}</strong>:
                        {{= $cError.text | ifNull($cError.code) }}
                    </li>
                {{ else }}
                    {{#
                        Steht die Berechnung des Mindestbestellwerts auf "sum", wird die Summe
                        aller Mindestbestellwerte geprüft. Dieser Fehler gehört zu keinem
                        einzelnen Gutschein und kommt daher ohne Gutschein-ID.
                    #}}
                    <li>{{= $cError.text | ifNull($cError.code) }}</li>
                {{ /if }}
            {{ /foreach }}
        </ul>
    </div>
{{ /if }}
```

<Info>
  Always check `details.voucherId` before rendering. The voucher ID is deliberately missing when the error comes from the summed minimum-order-value check. Which error is raised when is documented under [When each error occurs](/en/konfiguration/checkout-bestellablauf#wann-welcher-fehler-entsteht).
</Info>

***

## Free shipping method for voucher-only baskets

If a basket contains only (instant) vouchers, no physical shipping is required. For this case you can define a dedicated, always free shipping method under [`checkout.shippingMethod`](/en/konfiguration/checkout-bestellablauf#checkout-shippingmethod-versandarten): the `basicCost` scale sets the cost to `0` from a subtotal of `0`, and the validation [`shippingMethodValidation.productType`](/en/konfiguration/validierungs-und-prufservices) with `rule: deny` blocks the shipping method as soon as a regular product (product type `standard`) is in the basket — so it can only be selected for pure voucher baskets.

```json theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{
  "active": true,
  "basicCost": [
    {
      "cost": 0,
      "subtotal": 0
    }
  ],
  "description": "checkout.shippingMethod.INSTANT_VOUCHER.description",
  "group": null,
  "id": "INSTANT_VOUCHER",
  "image": "",
  "link": "",
  "name": "checkout.shippingMethod.INSTANT_VOUCHER.name",
  "orderText": "checkout.shippingMethod.INSTANT_VOUCHER.orderText",
  "taxable": true,
  "type": "standard",
  "validations": [
    {
      "options": {
        "rule": "deny",
        "ruleList": [
          "standard"
        ]
      },
      "service": "shippingMethodValidation.productType"
    }
  ],
  "weightCost": null
}
```

<Info>
  The value `standard` in the `ruleList` is the **value of the product type field** of the products (the product data field defined as product type via `content.usedFields`) — not to be confused with the shipping method's own `type: "standard"` parameter. Products for which the product type field is not set always pass the check.

  `name`, `description` and `orderText` refer to text snippets in this example, so the texts can be maintained per language via the text-snippet service.
</Info>

***

## Offering purchased vouchers for download

This example is not about redemption but about selling: if a [purchase voucher product](/en/admin-interface/katalog/produkte/kaufgutschein-produkt) was ordered, the shop generates a voucher code per ordered unit at order completion. The codes of a basket position are available in the template under `voucherIds`; the associated PDF is generated via the URL parameter `wsfilter=pdf` from the view template stored on the product.

The following block outputs a download link per position and code. It is suitable for the order confirmation page and for the email template of the order confirmation.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ foreach $basketItem in $wsBasket.items }}
   {{ if $basketItem.product.custom.voucherProductActive }}
      {{ foreach $voucherId in $basketItem.voucherIds }}
         <a href="{{= $wsViews.viewUrl('voucher/default_voucher.htm', {
             voucherId: $voucherId,
             productId: $basketItem.product.id,
             wsfilter: 'pdf'
         }, 'absolute') }}">
            Download voucher {{= $voucherId }}
         </a>
      {{ /foreach }}
   {{ /if }}
{{ /foreach }}
```

<Info>
  The type `absolute` generates a complete URL because emails cannot process relative links. The parameters `voucherId` and `productId` make the call independent of the session, so the link still works later on.

  `voucher/default_voucher.htm` stands here as a placeholder for the view template stored on the product in the "HTML template" field. How PDF views are built is described in [PDF views](/en/frontend/funktionsubersicht/pdf-ansichten).
</Info>

For further processing outside the shop, the same information is available in the order data under `data.orderList.item[].voucher`, see [API reference orders](/en/schnittstellen/admin-interface-api/api-referenz-bestellungen).


## Related topics

- [Vouchers](/en/admin-interface/marketing/gutscheine.md)
- [checkout - Order flow](/en/konfiguration/checkout-bestellablauf.md)
- [Practical examples - Linking with the payment provider](/en/frontend/praxisbeispiele/verknuepfung-zahlungsanbieter.md)
- [Insert codes](/en/frontend/funktionsubersicht/werbemittelkennzeichnung.md)
- [$wsVoucher - Vouchers](/en/frontend/referenz/module/wsvoucher.md)
