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

# Fynn Functions

> Mit Fynn Functions kannst du das Verhalten innerhalb von Fynn anpassen.

Fynn Functions erlaubt es den Entwicklern das Verhalten der Backend Logik zu erweitern.
Hier erfährst du wie du Fynn Functions einrichtest und verwendest.

<Note>Diese Erweiterung befindet sich aktuell in einer geschlossenen Beta-Phase. Bitte kontaktiere uns, um Zugang zu erhalten.</Note>

## Wie funktioniert Fynn Functions?

Fynn Functions erlaubt es dir, eigene Funktionen zu schreiben, die auf Ereignisse in Fynn reagieren.

Hierfür stellen wir verschiedene [Erweiterungs-Punkte](#erweiterungs-punkte) zur Verfügung, die du nutzen kannst, um eigene Logik zu schreiben.
Sofern für einen Erweiterungs-Punkt eine oder mehrere Funktionen registriert sind, wird diese bei dem entsprechenden Ereignis ausgeführt.
Hierbei wird ein Kontext-Objekt übergeben, das Informationen zum Ereignis enthält, dies kann bspw. ein Warenkorb sein.
Innerhalb der Funktion kannst du dann beliebige Aktionen ausführen, wie bspw. eine externe API aufrufen.
Jede Funktion muss am Ende eine oder mehrere vordefinierte [Operationen](#operationen) zurück geben, die anschließend von Fynn ausgeführt werden.

<Tip>
  Jeder Erweiterungs-Punkt hat unterschiedliche Kontext-Objekte und Operationen, die du verwenden kannst.
</Tip>

## Laufzeiten

Jede Funktion wird in einer isolierten Umgebung ausgeführt, die auf 5 Sekunden begrenzt ist. Bitte beachte, dass sich die Laufzeit einer Funktion direkt auf die Antwortzeit von Fynn auswirkt.

## Funktionalitäten in Fynn Functions

Innerhalb von Fynn Functions kannst du alle JavaScript Funktionen verwenden, die auch im Browser verfügbar sind.

## Erweiterungs-Punkte

Fynn Functions unterstützt aktuell folgende Erweiterungs-Punkte:

* [Checkout](#checkout): `checkout.cart.delivery`, `checkout.cart.cart_item_option`

### Lieferkosten hinzufügen

Der `checkout.cart.delivery` Erweiterungs-Punkt kann dazu verwendet werden, um Lieferkosten zum Warenkorb hinzuzufügen.
Ist der Preis der Lieferkosten einmalig, wird dieser nur in der 1. Rechnung berechnet und ist nicht wiederkehrend.
Andernfalls wird der Preis der Lieferkosten in jeder Rechnung berechnet.

#### Kontext-Objekt

<ResponseField name="context" type="DeliveryContext">
  <Expandable title="properties">
    <ResponseField name="cart" type="CartPublic">
      Der aktuelle Warenkorb des Benutzers
    </ResponseField>

    <ResponseField name="deliveryCountryCode" type="string">
      Der Ländercode des Lieferlandes, hierbei wird die Rechnungs- und Lieferadresse des Warenkorbs berücksichtigt.
    </ResponseField>
  </Expandable>
</ResponseField>

#### Operationen

<ResponseField name="checkout:add_delivery" type="AddDelivery">
  <Expandable title="properties">
    <ResponseField name="product" type="string">
      Die Produkt-ID die als Lieferkosten hinzugefügt werden soll.
    </ResponseField>

    <ResponseField name="pricePlan" type="string" required="false">
      Der Preisplan der für die Lieferkosten verwendet werden soll. Wird keiner angegeben, wird der 1. Preis im Produkt verwendet.
    </ResponseField>
  </Expandable>
</ResponseField>

#### Beispiel

```typescript theme={null}
function run (input) {
    const countryCode = input?.deliveryCountryCode;
    const itemsCount = input?.cart?.items.length;

  if (itemsCount === 0) return [];

    if (countryCode === 'DE') {
        return [{
            __type: 'checkout:add_delivery',
            product: '14937550-26d0-454a-b4d4-64eabac67663'
        }];
    }

    if (countryCode === 'CH') {
        return [{
            __type: 'checkout:add_delivery',
            product: '5a7f267a-087b-4bdf-a620-79a4e2942545'
        }];
    }

    // more custom logic, e.g. by zip, best price, etc.

    return [];
}
```

### Produkt-Optionen hinzufügen

Der `checkout.cart.cart_item_option` Erweiterungs-Punkt kann dazu verwendet werden, um Auswahl-Optionen im Warenkorb am Produkt anzuzeigen.

<Frame>
  <img src="https://mintcdn.com/fynnsubscriptionbilling/iVIPUuF_nH0B0-xL/images/checkout%E2%80%93options.png?fit=max&auto=format&n=iVIPUuF_nH0B0-xL&q=85&s=b727c193872eefba3cdfc2a45b405c8e" alt="Checkout Optionen" width="467" height="177" data-path="images/checkout–options.png" />
</Frame>

#### Kontext-Objekt

<ResponseField name="product" type="CartItemProductInput">
  <Expandable title="properties">
    <ResponseField name="id" type="string">
      Die ID des Produktes
    </ResponseField>

    <ResponseField name="number" type="string">
      Die Produktnummer, sofern diese angegeben ist. Andernfalls ist diese `null`.
    </ResponseField>

    <ResponseField name="customFields" type="array">
      Werte der Benutzerdefinierten Attribute des Produktes.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="context" type="CartItemInput">
  <Expandable title="properties">
    <ResponseField name="cartItem" type="CartPublicItem">
      Das aktuelle Produkt / Warenkorb-Item im Warenkorb
    </ResponseField>

    <ResponseField name="product" type="CartItemProductInput">
      Die aktuelle Produkt-ID
    </ResponseField>
  </Expandable>
</ResponseField>

#### Operationen

<ResponseField name="checkout:add_cart_item_option_set" type="AddCartItemOptionSet">
  <Expandable title="properties">
    <ResponseField name="product" type="string" required="true">
      Das Produkt für das die Optionen hinzugefügt werden sollen.
    </ResponseField>

    <ResponseField name="name" type="string" required="true">
      Der Name der Optionsauswahl, welcher im Webhook verfügbar ist.
    </ResponseField>

    <ResponseField name="label" type="string" required="true">
      Der Name der Optionsauswahl, welcher im Checkout angezeigt wird.
    </ResponseField>

    <ResponseField name="options" type="array" required="true">
      Die Optionen die zur Auswahl stehen.
    </ResponseField>
  </Expandable>
</ResponseField>

Alternativ können Produkt-Optionen auf Basis eines [Benutzerdefiniertes Attributes](/guide/tenant/custom-fields) im Produkt hinzugefügt werden.

<ResponseField name="checkout:add_custom_field_list:as_option_set" type="AddCustomFieldListAsOptionSet">
  <Expandable title="properties">
    <ResponseField name="name" type="string" required="true">
      Der Name der Optionsauswahl, welcher im Webhook verfügbar ist.
    </ResponseField>

    <ResponseField name="label" type="string" required="true">
      Der Name der Optionsauswahl, welcher im Checkout angezeigt wird.
    </ResponseField>

    <ResponseField name="customField" type="string" required="true">
      Das Feld welches im Produkt als [Benutzerdefiniertes Attribut](/guide/tenant/custom-fields) (Typ: Liste) verfügbar sein muss,
      um dessen Listen-Optionen anzuzeigen.
    </ResponseField>

    <ResponseField name="preselectRegex" type="string" required="false">
      Regex, um eine Listen-Option als Standard vorauszuwählen. Bspw. `/.* \(Aktuell\)/i` (muss (Aktuell) beinhalten).
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField type="AddCartItemOption">
  <Expandable title="properties">
    <ResponseField name="preselected" type="boolean" default="false">
      Ob die Option vorausgewählt ist.
    </ResponseField>

    <ResponseField name="label" type="string" required="true">
      Der Name der Option, welcher im Checkout angezeigt wird.
    </ResponseField>

    <ResponseField name="value" type="string" required="true">
      Der Wert der Option, welcher im Webhook verfügbar ist.
    </ResponseField>
  </Expandable>
</ResponseField>

#### Beispiel

```typescript theme={null}
function run(input) {
  return [
    {
      __type: 'checkout:add_cart_item_option_set',
      product: '0ba377f0-d9e9-4338-a107-9209a6e0f1fb',
      name: 'ab Ausgabe',
      label: 'ab Ausgabe',
      options: [
        {
          label: 'Nr. 24',
          value: '24'
        },
        {
          label: 'Nr. 25',
          value: '25',
          preselected: true,
        },
        {
          label: 'Nr. 26',
          value: '26'
        },
      ]
    }
  ];
}
```

## Funktion registrieren

Um eine Funktion zu registrieren, erstelle ein Support-Ticket um den Zugang zu Fynn Functions zu erhalten.

## Globale Kontext-Objekte

```typescript theme={null}
export type CartItemProduct

export type CartPublicCustomerAddress = {
  id: string;
  firstName?: string;
  lastName?: string;
  companyName?: string;
  street: string;
  houseNumber?: string;
  zip: string;
  city: string;
  addition?: string;
  countryCode: string;
  costCentre?: string;
  vatId?: string;
}

export type CartPublicItem = {
  name: string;
  description?: string;
  quantity: number;
  type: 'product' | 'plan' | 'delivery';
  id: string;
  product: {
      id: string;
      number: string | null;
      customFields: Record<string, any>;
  },
  periods?: {
    contractPeriod: string;
    cancellationPeriod: string;
  }[],
  quantityDetails: {
    aggregationType: 'count' | 'count_unique' | 'max' | 'sum' | 'average' | 'last_value';
    unit: string;
    description?: string;
    quantityEditable: boolean;
  },
  price: {
    currencyCode: string;
    taxRate?: number;
    totalNetAmount: number;
    type: 'recurring' | 'instant_metered' | 'metered';
    calculationType: 'flat_fee' | 'per_unit' | 'tiered' | 'volume' | 'stair_step' | 'percentage';
    payInAdvance: boolean;
    freeUnits?: number;
    price: {
      amount?: number;
      items?: {
        from: number;
        to?: number;
        amount: number;
        flatAmount: number;
      },
      percentage?: number;
      fixedAmount?: number;
    };
    recurring?: {
      interval: 'day' | 'week' | 'month' | 'year';
      intervalCount?: number;
      trialPeriodDays?: number;
      recurringAmount?: number;
    }
  },
  optionSets: CartPublicItemOptionSet[];
}

export type CartPublicItemOptionSet = {
  label: string;
  name: string;
  options: CartPublicItemOption[];
}

export type CartPublicItemOption = {
  label: string;
  value: string | number | boolean;
  preselected: boolean;
  product?: string | null;
  pricePlan?: string | null;
}

export type CartDiscountDetails = {
  discount: {
    code: string;
    name: string;
    type: 'percentage' | 'fixed_amount';
    percentage?: number;
    fixedAmount?: string;
    frequency: 'once' | 'limited' | 'lifetime';
    frequencyInterval?: number;
  };
  totalAmount: string;
  totalNetBeforeDiscount: string;
};

export type CartPublicPrice = {
  amountDue: number;
  currencyCode: string;
  netAmount?: number;
  taxes?: {
    netAmount: number;
    taxAmount: number;
    rate: number;
  }[];
  discountDetails?: CartDiscountDetails;
}

export type CartPublicSettings = {
  allowCoupons: boolean;
  forceCompany: boolean;
  backButton?: {
    label?: string;
    url: string;
  };
  showDeliveryAddress: boolean;
}

export type CartPublic = {
  id: string;
  customer?: string;
  email?: string;
  invoiceAddress?: CartPublicCustomerAddress;
  deliveryAddress?: CartPublicCustomerAddress;
  items: CartPublicItem[];
  price: CartPublicPrice;
  completionDetails?: {
    invoice: {
      number: string;
      downloadLink: string;
      amount: string;
    } | null,
    payment: {
      reference: string | null;
      method: string;
      bankAccount?: {
        iban: string;
        bic: string;
        bankName: string;
        accountHolder: string;
        reference: string;
      },
      qrCode?: string;
    },
    subscription?: {
      number: string;
    },
    confirmationMessage?: string;
    backToProviderUrl?: string | null;
  },
  settings: CartPublicSettings;
}
```
