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

# Materialien Abo-Modul

> Vollständige Template-Beispiele für das Abo-Modul: Abo-Abschluss ohne Seitenwechsel, mit Modal-Fenstern, AJAX und abhängigen Varianten.

Diese Seite enthält ausführliche Template-Beispiele für das Abo-Modul. Sie ergänzen die Anleitung um eine vollständige Umsetzung, die Sie als Ausgangspunkt übernehmen können.

Die gezeigte Lösung vermeidet Weiterleitungen auf `ws_bank_manage.htm` und `ws_address_manage.htm`. Der Käufer bleibt auf `ws_product.htm` beziehungsweise `ws_subscription_manage.htm`, Adresse und Bankverbindung bearbeitet er in Modal-Fenstern. Abhängige Varianten sind berücksichtigt.

> [!NOTE]
>
> Die Modal-Fenster sind mit Bootstrap 4 umgesetzt. Die Funktionen arbeiten auch mit anderen Bootstrap-Versionen zusammen.
>
> Die Beispiele setzen jQuery und die WEBSALE-Funktion `ws_AJAXloadTemplate` voraus. Letztere steckt im Modul zum Nachladen von Templates oder in der System- beziehungsweise Core-Bibliothek.

Wegweiser: [Abo-Modul](https://dokumentation.websale.de/v8s/marketing-funktionen/abo-modul.md) · Referenz: SUBS-Tags, [WEBSALE JavaScript-Bibliothek](https://dokumentation.websale.de/v8s/materialien/websale-javascript-bibliothek.md)

> [!WARNING]
>
> Die folgenden Listings sind Auszüge. Die drei Punkte am Anfang und am Ende stehen für den übrigen Inhalt des jeweiligen Templates, der unverändert bleibt.
>
> Alle Beschriftungen und Meldungstexte sind Beispieltexte. Ersetzen Sie sie durch Ihre eigenen oder durch Textplatzhalter aus der `shop.config`.

## Die fünf Templates im Überblick

| Schritt | Template | Aufgabe |
| - | - | - |
| 1 | `ws_product.htm` | Abo-Produkt mit Lieferintervall, Adresse, Bankverbindung und den drei Modal-Fenstern |
| 2 | `ws_login.htm` | Anmeldung und Registrierung im Modal-Fenster |
| 3 | `ws_address_manage.htm` | Rechnungsadresse im Modal-Fenster bearbeiten |
| 4 | `ws_bank_manage.htm` | Bankverbindung im Modal-Fenster bearbeiten |
| 5 | `ws_subscription_manage.htm` | neue Abo-Übersicht im Kundenkonto |

Die Reihenfolge ist keine Empfehlung, sondern eine Abhängigkeit: Schritt 1 ruft die Templates aus den Schritten 2 bis 4 per AJAX nach. Bauen Sie deshalb zuerst Schritt 1 und ergänzen Sie die übrigen danach.

## Schritt 1: Produktdetailansicht

Dieses Template trägt die Hauptlast. Es zeigt das Abo-Produkt, prüft über `DC-FPAboOrderOk`, ob alle Voraussetzungen erfüllt sind, und hält die drei Modal-Fenster für Anmeldung, Adresse und Bankverbindung bereit.

> [accordion] ws\_product.htm
>
> ```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
> ...
> {PR-SubscriptionProduct}
>
>    <!-- Wird benötigt, wenn das Abo-Produkt abhängige Varianten besitzt -->
>    ~PR-PrintHiddenDepVariations~
>    <!-- Anzeige der Produktvarianten, beispielsweise aus der incl_fast_includes.htm -->
>    ~WS-Fast_Include(Incl-Variants)~
>
>    Bitte wählen Sie Ihre Lieferung:<br>
>
>    Einmalige Lieferung<br>
>    {!PR-DepVarQuantity}
>       <label for="~PR-Quantity_Input~" class="control-label">Menge</label><br>
>       <input type="text" name="~PR-Quantity_Input~" value="~PR-Quantity~" autocomplete="false" class="form-control">
>    {/!PR-DepVarQuantity}
>
>    {PR-DepVarQuantity}
>       <input type="hidden" name="~PR-Quantity_Input~" value="1" autocomplete="false">
>    {/PR-DepVarQuantity}
>
>    {ST-DepVar_OK}
>       <button type="submit" name="~BT-ProductAddToBasket_Ajax~" data-ws-ajax="basket">Warenkorb</button><br>
>    {/ST-DepVar_OK}
>
>    {!ST-LoggedIn}
>       Sie benötigen ein Kundenkonto, um ein Abonnement bei uns abschließen zu können.<br>
>       <span onclick="$('#modal-loginFields').modal();">Melden Sie sich jetzt an</span><br>
>    {/!ST-LoggedIn}
>
>    {ST-LoggedIn}
>       {SUBS-CycleSelection}
>          Lieferintervall<br>
>          <select name="~SUBS-CycleSelection_input~" onchange="SubmitSelection(); return false;">
>             {@SUBS-CycleSelection_data}
>                <option value="~SUBS-CycleSelection_data_value~" ~SUBS-CycleSelection_data_selected~>~SUBS-CycleSelection_data_descr~</option>
>             {/@SUBS-CycleSelection_data}
>          </select><br>
>       {/SUBS-CycleSelection}
>
>       {!A-FirstName}
>          <span data-toggle="modal" data-target="#modal-addressFields">Rechnungsadresse erstellen</span><br>
>       {/!A-FirstName}
>
>       {A-FirstName}
>          <div id="subscriptionAddress"></div><br>
>          <span data-toggle="modal" data-target="#modal-addressFields">Adresse bearbeiten</span><br>
>          <div id="subscriptionBank"></div><br>
>          <span data-toggle="modal" data-target="#modal-bankFields">Bankverbindung bearbeiten</span>
>       {/A-FirstName}
>    {/ST-LoggedIn}
>
>    {!A-FirstName}~DC-FPAboOrderOk_set(false)~{/!A-FirstName}
>    {!SUBS-BankData}~DC-FPAboOrderOk_set(false)~{/!SUBS-BankData}
>    {!SUBS-CycleSelection}~DC-FPAboOrderOk_set(false)~{/!SUBS-CycleSelection}
>
>    <input {DC-FPAboOrderOk(false)}disabled{/DC-FPAboOrderOk(false)} name="~BT-Subscription~" value="Abo aktivieren und zahlungspflichtig bestellen" type="submit">
>
>    <!-- Modal Fenster steht außerhalb des Produktforms stehen -->
>    <div class="modal fade" id="modal-loginFields" tabindex="-1" role="dialog">
>       <div class="modal-dialog modal-lg" role="document">
>          <div class="modal-content" id="modal-content-loginFields">
>             <div class="modal-body">
>                <div class="row">
>                   <div class="col-12">
>                      <h1 style="float: left;">Melden Sie sich an</h1>
>                      <button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="wsManualModalCloseLogin();"><span aria-hidden="true">×</span></button>
>                   </div>
>                </div>
>                <div id="modal-body-loginFieldsError" class="row"></div>
>                <div id="modal-body-loginFields" class="row"></div>
>             </div>
>          </div>
>       </div>
>    </div>
>
>    <div class="modal fade" id="modal-addressFields" tabindex="-1" role="dialog">
>       <div class="modal-dialog modal-lg" role="document">
>          <div class="modal-content" id="modal-content-addressFields">
>             <div class="modal-body">
>                Adresse bearbeiten
>                <button type="button" class="close" data-dismiss="modal" aria-label="Close" onclick="wsManualModalClose();"><span aria-hidden="true">×</span></button>
>                <div id="modal-body-addressFields"></div>
>             </div>
>         </div>
>       </div>
>    </div>
>    <div class="modal fade" id="modal-bankFields" tabindex="-1" role="dialog">
>       <div class="modal-dialog modal-lg" role="document">
>          <div class="modal-content" id="modal-content-bankFields">
>             <div class="modal-body">
>                Bankdetails bearbeiten
>                <button type="button" data-dismiss="modal" aria-label="Close" onclick="wsManualModalCloseBank();"><span aria-hidden="true">×</span></button>
>                <div id="modal-body-bankFields">
>                   <div id="bankManageIframe">
>                      <iframe src="~WS-EnsureNextLinkContainsURLParams~~WS-BankManageLink~&otp3=updateBankSubscription" frameborder="0" allowfullscreen class="iframe" style="height:605px;width:100%"></iframe>
>                   </div>
>                   <a onclick="wsManualModalCloseBank();">Fertig</a>
>                </div>
>             </div>
>          </div>
>       </div>
>    </div>
>
>    {ST-LoggedIn}
>      <script>
>        $(document).on("click","#modal-addressFields",function(e){
>          if(!$(e.target).is("#modal-addressFields *")) {
>            wsManualModalClose();
>          }
>        })
>
>        $(document).on("click","#modal-bankFields",function(e){
>          if(!$(e.target).is("#modal-bankFields *")) {
>            wsManualModalCloseBank();
>          }
>        })
>
>        function wsManualModalClose(){
>          $("#modal-addressFields").modal("hide");
>        }
>
>        function wsManualModalCloseBank(){
>          $("#modal-bankFields").modal("hide");
>          ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~WS-BankManageLink~&otp3=updateBankSubscription', '~WS-Charset~', wsAjaxLoadBankStart, wsAjaxLoadBankError, 'wsAjaxLoadBankResponseSuccess()', 'wsAjaxLoadBankResponseError()');
>        }
>
>        function wsAjaxLoadAdrressStart(){}
>        function wsAjaxLoadAdrressError(){}
>        function wsAjaxLoadAdrressResponseSuccess(){}
>        function wsAjaxLoadAdrressResponseError(){}
>        function wsAjaxLoadAdrressSaveResponseSuccess(){}
>        ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~WS-AddrManageLink~&otp3=updateAddressSubscription', '~WS-Charset~', wsAjaxLoadAdrressStart, wsAjaxLoadAdrressError, 'wsAjaxLoadAdrressResponseSuccess()', 'wsAjaxLoadAdrressResponseError()');
>
>        function wsAjaxLoadBankStart(){}
>        function wsAjaxLoadBankError(){}
>        function wsAjaxLoadBankResponseSuccess(){}
>        function wsAjaxLoadBankResponseError(){}
>        ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~WS-BankManageLink~&otp3=updateBankSubscription', '~WS-Charset~', wsAjaxLoadBankStart, wsAjaxLoadBankError, 'wsAjaxLoadBankResponseSuccess()', 'wsAjaxLoadBankResponseError()');
>      </script>
>    {/ST-LoggedIn}
>
>    {!ST-LoggedIn}
>       {DC-SPRegisterParamsOTP1}
>          ~DC-FPRegisterParamsOTP1_set(otp1=$DC-SPRegisterParamsOTP1$)~
>       {/DC-SPRegisterParamsOTP1}
>
>       {DC-SPRegisterParamsOTP3}
>          ~DC-FPRegisterParamsOTP3_set(otp3=$DC-SPRegisterParamsOTP3$)~
>       {/DC-SPRegisterParamsOTP3}
>
>       {!DC-CompareStringInList($DC-SPRegisterParamsOTP3$,loginSubscription)}
>          ~DC-FPRegisterParamsOTP3_set(otp3=loginSubscription)~
>       {/!DC-CompareStringInList($DC-SPRegisterParamsOTP3$,loginSubscription)}
>
>       {DC-SPRegisterParamsOTPLoginType}
>          ~DC-FPRegisterParamsOTPLoginType_set(otplogintype=$DC-SPRegisterParamsOTPLoginType$)~
>       {/DC-SPRegisterParamsOTPLoginType}
>
>       <script>
>         function wsAjaxLoginStart(){}
>         function wsAjaxLoginError(){}
>         function wsAjaxLoginResponseSuccess(){}
>         function wsAjaxLoginResponseError(){}
>
>         function ws_AJAXSendLoginForSubProductStart(){};
>         function ws_AJAXSendLoginForSubProductError(){};
>         function ws_AJAXSendLoginForSubProductResponseSuccess(){
>           ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~PR-DepVarLink~', '~WS-Charset~', ws_AJAXPRStart, ws_AJAXPRError, 'ws_AJAXPRSubProductResponseSuccess()', 'ws_AJAXPRResponseError()');
>         };
>         function ws_AJAXSendLoginForSubProductResponseError(){};
>
>         function ws_AJAXSendRegisterForSubProductStart(){};
>         function ws_AJAXSendRegisterForSubProductError(){};
>         function ws_AJAXSendRegisterForSubProductResponseSuccess(){
>           ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~PR-DepVarLink~&otp3=loginSubscription&type=loginUser&otplogintype=newuser&otp1=NewUserSubscription', '~WS-Charset~', ws_AJAXPRStart, ws_AJAXPRError, 'ws_AJAXPRSubProductResponseSuccess()', 'ws_AJAXPRResponseError()');
>         };
>         function ws_AJAXSendRegisterForSubProductResponseError(){};
>
>         function ws_AJAXPRSubProductResponseSuccess(){}
>
>         ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~WS-LoginLink~{DC-FPRegisterParamsOTP1}&~DC-FPRegisterParamsOTP1~{/DC-FPRegisterParamsOTP1}{DC-FPRegisterParamsOTP3}&~DC-FPRegisterParamsOTP3~{/DC-FPRegisterParamsOTP3}{DC-FPRegisterParamsOTPLoginType}&~DC-FPRegisterParamsOTPLoginType~{/DC-FPRegisterParamsOTPLoginType}', '~WS-Charset~', wsAjaxLoginStart, wsAjaxLoginError, 'wsAjaxLoginResponseSuccess()', 'wsAjaxLoginResponseError()');
>       </script>
>    {/!ST-LoggedIn}
>
>    {ST-LoggedIn}
>      <script>
>        function ws_AJAXPRSubProductResponseSuccess(){
>          $("#modal-loginFields").modal("hide");
>        }
>      </script>
>    {/ST-LoggedIn}
>
>    <script>
>      function ws_AJAXregisterNewUserStart(){}
>      function ws_AJAXregisterNewUserResponseSuccess(){}
>      function ws_AJAXregisterNewUserResponseError(){}
>      function ws_AJAXregisterNewUserError(){}
>      function wsManualModalCloseLogin(){
>        $("#modal-loginFields").modal("hide");
>      }
>      // Formulare per Ajax nachladen
>      function ws_AJAXsendFormTemplateStart() {}
>      function ws_AJAXsendFormTemplateResponseSuccess() {}
>      function ws_AJAXsendFormTemplateResponseError() {}
>      function ws_AJAXsendFormTemplateError() {}
>    </script>
>
> {/PR-SubscriptionProduct}
> ...
> ```

> [!NOTE]
>
> Der Schalter `DC-FPAboOrderOk` ist der Kern der Logik. Drei Prüfungen setzen ihn auf `false`: fehlende Rechnungsadresse, fehlende Bankverbindung und fehlende Intervallauswahl. Erst wenn alle drei bestanden sind, ist die Schaltfläche zum Abschluss nicht mehr deaktiviert.
>
> Wenn Sie weitere Voraussetzungen haben, ergänzen Sie einfach eine weitere Zeile nach demselben Muster.

## Schritt 2: Anmeldung im Modal-Fenster

`ws_login.htm` liefert die Anmelde- und Registrierungsfelder, die Schritt 1 per AJAX in das Modal-Fenster holt. Der gesamte Abo-Teil steckt im One-Time-Parameter `WS-OTP3(loginSubscription)`. Die negative Klammerung darüber sorgt dafür, dass der reguläre Seiteninhalt nicht mitgeliefert wird, wenn das Template als AJAX-Fragment angefordert wird.

Innerhalb davon unterscheidet `WS-OTP1(NewUserSubscription)` zwischen Anmeldung eines bestehenden Kunden und Neuregistrierung. Beide Formulare verweisen über `ws_AJAXloadTemplate` aufeinander, sodass der Käufer im Modal-Fenster zwischen den beiden Ansichten wechseln kann, ohne die Produktseite zu verlassen.

> [accordion] ws\_login.htm
>
> ```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
> ...
> {!WS-OTP3(loginSubscription)}
>    ...
>    <!-- Inhalt, der auf ws_login.htm angezeigt wird und nicht für das Modal-Fenster oder Abo-Modul verwendet wird. -->
>    ...
> {/!WS-OTP3(loginSubscription)}
>
> {WS-OTP3(loginSubscription)}
> {ST-Ajax}
>    <WS-Ajax-modal-body-loginFields>
>    {!WS-OTP1(NewUserSubscription)}
>       <form action="~FORM-QuickLogin~" method="post" id="quicklogin_subProduct_input">
>          <input type="hidden" name="otp1" value="KnownUserSubscription">
>          <input type="hidden" name="otp3" value="loginSubscription">
>          <input type="hidden" name="otplogintype" value="newuser">
>          ~DC-FPQuickloginID_set(quicklogin_subProduct_input)~
>
>          <div class="{WS-Email_error}has-error has-feedback{/WS-Email_error}">
>             <div class="inputLabelFocus" data-placeholder="E-Mail-Adresse *">
>                <label for="~WS-Email_input~" class="control-label">E-Mail-Adresse *</label>
>                <input type="email" name="~WS-Email_input~" value="~WS-Email~" maxlength="80" class="input~WS-Email_error~ form-control">
>                {WS-Email_error}<span class="form-control-feedback" aria-hidden="true"></span>{/WS-Email_error}
>             </div>
>          </div>
>          <div class="has-feedback {WS-PW_error}has-error{/WS-PW_error}">
>             <div class="inputLabelFocus" data-placeholder="Passwort *">
>                <label for="~WS-PW_input~" class="control-label">Passwort *</label>
>                <input type="password" maxlength="128" name="~WS-PW_input~" value="~WS-PW~" class="input~WS-PW_error~ form-control">
>             </div>
>          </div>
>          <button type="submit" onclick="return ws_AJAXsendFormTemplate('~DC-FPQuickloginID~', '~WS-CharSet~', ws_AJAXSendRegisterForSubProductStart, ws_AJAXSendRegisterForSubProductError, 'ws_AJAXSendRegisterForSubProductResponseSuccess()', 'ws_AJAXSendRegisterForSubProductResponseError()');">Anmelden</button>
>          <span onclick="ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~WS-LoginLink~&otp3=loginSubscription&type=loginUser&otplogintype=newuser&otp1=NewUserSubscription&act=subscription&otpsubscriptionprodindex=~PR-ProdIndex~{PR-DepVariations}&otpsubscriptionvarindex=~PR-VarIndex~{/PR-DepVariations}', '~WS-Charset~', wsAjaxLoginStart, wsAjaxLoginError, 'wsAjaxLoginResponseSuccess()', 'wsAjaxLoginResponseError()');">Kundenkonto erstellen</span>
>       </form>
>       ~DC-SPRegisterParamsOTP1_set($WS-OTP1$)~
>       ~DC-SPRegisterParamsOTP3_set($WS-OTP3$)~
>       ~DC-SPRegisterParamsOTPLoginType_set($WS-OTPlogintype$)~
>    {/!WS-OTP1(NewUserSubscription)}
>
>    {WS-OTP1(NewUserSubscription)}
>       <form action="~FORM-LoginNewUser~" id="ws_newuser_content_subscription" method="Post" autocomplete="nono" data-ws-ajax="NewUserLoginSubscription">
>          ~DC-FPQuickloginID_set(ws_newuser_content_subscription)~
>          <input type="hidden" name="otp1" value="NewUserSubscription">
>          <input type="hidden" name="otp3" value="loginSubscription">
>          <input type="hidden" name="otplogintype" value="newuser">
>
>          <div class="{WS-NewEmail_error} is-invalid{/WS-NewEmail_error}">
>             <div class="inputLabelFocus" data-placeholder="E-Mail Adresse">
>                <label for="~WS-NewEmail_input~" class="control-label">E-Mail Adresse *</label>
>                <input type="email" name="~WS-NewEmail_input~" value="~WS-NewEmail~" maxlength="128" class="form-control{WS-NewEmail_error} is-invalid{/WS-NewEmail_error}">
>             </div>
>          </div>
>
>          <div class="{WS-NewPW_error} is-invalid{/WS-NewPW_error}">
>             <div class="inputLabelFocus" data-placeholder="Passwort">
>                <label for="~WS-NewPW_input~" class="control-label">Passwort *</label>
>                <input type="password"  maxlength="128" name="~WS-NewPW_input~" value="~WS-NewPW~" class="form-control{WS-NewPW_error} is-invalid{/WS-NewPW_error}" data-container="body" data-toggle="popover" data-placement="top" data-trigger="focus" data-content="Ihr Passwort sollte mindestens 8 Zeichen lang sein und aus einer Buchstaben-Zahlen-Kombination bestehen.">
>             </div>
>             ~WS-Fast_Include(Incl-WSLoginPasswordChecker)~
>          </div>
>
>          <div class="{WS-PWAgain_error} is-invalid{/WS-PWAgain_error}">
>             <div class="inputLabelFocus" data-placeholder="Passwort wiederholen *">
>                <label for="~WS-PWAgain_input~" class="control-label">Passwort wiederholen *</label>
>                <div class="input-group">
>                   <input type="password" maxlength="128" name="~WS-PWAgain_input~" value="~WS-PWAgain~" class="form-control{WS-PWAgain_error} is-invalid{/WS-PWAgain_error}" data-container="body" data-toggle="popover" data-placement="top" data-trigger="focus" data-content="Ihr Passwort sollte mindestens 8 Zeichen lang sein und aus einer Buchstaben-Zahlen-Kombination bestehen.">
>                   <button type="submit" onclick="return ws_AJAXsendFormTemplate('~DC-FPQuickloginID~', '~WS-CharSet~', ws_AJAXSendRegisterForSubProductStart, ws_AJAXSendRegisterForSubProductError, 'ws_AJAXSendRegisterForSubProductResponseSuccess()', 'ws_AJAXSendRegisterForSubProductResponseError()');"><b>Submit</b></button>
>                </div>
>             </div>
>          </div>
>       </form>
>
>       <span onclick="ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~WS-LoginLink~&otp3=loginSubscription&type=loginUser&otplogintype=newuser&act=subscription&otpsubscriptionprodindex=~PR-ProdIndex~{PR-DepVariations}&otpsubscriptionvarindex=~PR-VarIndex~{/PR-DepVariations}', '~WS-Charset~', wsAjaxLoginStart, wsAjaxLoginError, 'wsAjaxLoginResponseSuccess()', 'wsAjaxLoginResponseError()');">Ich habe bereits ein Kundenkonto</span>
>       ~DC-SPRegisterParamsOTP1_set($WS-OTP1$)~
>       ~DC-SPRegisterParamsOTP3_set($WS-OTP3$)~
>       ~DC-SPRegisterParamsOTPLoginType_set($WS-OTPlogintype$)~
>    {/WS-OTP1(NewUserSubscription)}
>    </WS-Ajax-modal-body-loginFields>
> {/ST-Ajax}
> {/WS-OTP3(loginSubscription)}
> ...
> ```

> [!NOTE]
>
> Die drei `DC-SPRegisterParams…_set`-Zeilen merken sich die One-Time-Parameter in Session-Parametern. Nur dadurch findet der Käufer nach der Registrierung oder nach einer Bestätigungsmail wieder in den Abo-Kontext zurück.
>
> Die Links tragen zusätzlich `otpsubscriptionprodindex` und, bei abhängigen Varianten, `otpsubscriptionvarindex`. Damit weiß das Modal-Fenster nach dem Wechsel zwischen Anmeldung und Registrierung noch, um welches Produkt es ging.

## Schritt 3: Rechnungsadresse im Modal-Fenster

`ws_address_manage.htm` wird für das Abo-Modul an zwei Stellen gebraucht. Der obere Block in `ST-Ajax` liefert die reine Adressanzeige, die Schritt 1 und Schritt 5 in ihren Container `subscriptionAddress` holen. Der untere Block liefert das Adressformular für das Modal-Fenster.

Wieder trennt ein One-Time-Parameter die beiden Welten, diesmal `WS-OTP3(updateAddressSubscription)`. Das normale Adressformular der Seite bleibt negativ geklammert erhalten, damit das Kundenkonto weiter wie gewohnt funktioniert.

> [accordion] ws\_address\_manage.htm
>
> ```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
> ...
> {ST-Ajax}
>    <!-- Adressfelder. Werden auf ws_product.htm angezeigt -->
>    <WS-Ajax-subscriptionAddress>
>       {A-Salutation}~A-Salutation~ ~A-FirstName~{A-LastName} ~A-LastName~{/A-LastName}{/A-Salutation}
>       {A-Street1}~A-Street1~{/A-Street1}
>       {A-Zip}~A-Zip~{/A-Zip}{A-City} ~A-City~{/A-City}
>    </WS-Ajax-subscriptionAddress>
> {/ST-Ajax}
> ...
> {A-Edit}
>
>    <!-- negativ geklammert, damit das Formular nicht im Modal-Fenster gezeigt wird -->
>    {!WS-OTP3(updateAddressSubscription)}
>       <div id="addressformwrapper">
>          {ST-Ajax}<WS-Ajax-addressformwrapper>{/ST-Ajax}
>             <form action="~FORM-AddrManage~" method="post" autocomplete="" id="addressform">
>                ...
>             </form>
>          {ST-Ajax}</WS-Ajax-addressformwrapper>{/ST-Ajax}
>       </div>
>    {/!WS-OTP3(updateAddressSubscription)}
>
>    <!-- Formular im Modal-Fenster -->
>    {WS-OTP3(updateAddressSubscription)}
>       {ST-Ajax}<WS-Ajax-modal-body-addressFields>{/ST-Ajax}
>          <form action="~FORM-AddrManage~" method="post" autocomplete="" id="addressform">
>             <input type="hidden" name="otp3" value="updateAddressSubscription">
>             ~WS-Fast_Include(Incl-InvoiceAddressMsg)~
>             Felder, die mit einem "*" gekennzeichnet sind, sind erforderlich.
>             ~WS-Fast_Include(Incl-InvoiceAddressFields)~
>             <button type="submit" name="~BT-Addr_Save~" onclick="return (ws_AJAXsendFormTemplate('addressform', '~WS-CharSet~', ws_AJAXsendFormTemplateStart, ws_AJAXsendFormTemplateError, 'ws_AJAXsendFormTemplateResponseSuccess()', 'ws_AJAXsendFormTemplateResponseError()'));">Speichern</button>
>          </form>
>       {ST-Ajax}</WS-Ajax-modal-body-addressFields>{/ST-Ajax}
>    {/WS-OTP3(updateAddressSubscription)}
>
> {/A-Edit}
> ...
> ```

> [!WARNING]
>
> Beide Formulare tragen dieselbe ID `addressform`. Das geht nur gut, weil die beiden Blöcke durch den One-Time-Parameter nie gleichzeitig ausgegeben werden. Wenn Sie die negative Klammerung entfernen, steht die ID zweimal im Dokument und das Speichern im Modal-Fenster greift auf das falsche Formular zu.

## Schritt 4: Bankverbindung im Modal-Fenster

`ws_bank_manage.htm` ist das umfangreichste der vier nachgeladenen Templates, weil es drei Zustände abdecken muss: eine bereits gewählte Bankverbindung anzeigen, aus den vorhandenen Bankverbindungen auswählen, und eine Bankverbindung anlegen oder bearbeiten.

Die gesamte Auswahl hängt an `SUBS-FixedPaymentCodeUsed(4)`. Der Wert 4 ist der Zahlungsarten-Code für Lastschrift. Das Modul fragt damit ab, ob in der `shop.config` eine feste Zahlungsart für Abos eingestellt ist und ob es die Lastschrift ist. Nur dann braucht es überhaupt eine Bankverbindung.

> [accordion] ws\_bank\_manage.htm
>
> ```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
> ...
> <main>
>    {ST-Ajax}
>       <WS-Ajax-subscriptionBank>
>          {SUBS-BankData}
>             {Bank-UserDescr}<span>~Bank-UserDescr~</span><br>{/Bank-UserDescr}
>             {BANK-Owner}<span>~BANK-Owner~</span><br>{/BANK-Owner}
>             {BANK-IBAN}<span>~BANK-IBAN~</span><br>{/BANK-IBAN}
>             {BANK-BIC}<span>~BANK-BIC~</span><br>{/BANK-BIC}
>             {BANK-Bank}<span>~BANK-Bank~</span>{/BANK-Bank}
>             {SUBS-BankIndex_input}<input type="hidden" name="~SUBS-BankIndex_input~" value="">{/SUBS-BankIndex_input}
>          {/SUBS-BankData}
>          {!SUBS-BankData}
>             Um Ihr Abonnement abzuschließen, wählen Sie bitte eine Bankverbindung aus, über die Ihr Abonnement zukünftig bezahlt werden soll.
>             <span data-toggle="modal" data-target="#modal-bankFields">Bankverbindung auswählen</span>
>          {/!SUBS-BankData}
>       </WS-Ajax-subscriptionBank>
>    {/ST-Ajax}
>
>    {!WS-OTP3(updateBankSubscription)}
>       <!-- Alle Inhalte, die nicht im Modal-Fenster gezeigt werden sollen -->
>    {/!WS-OTP3(updateBankSubscription)}
>
>    <!-- Inhalt wird im Modal-Fenster angezeigt -->
>    {WS-OTP3(updateBankSubscription)}
>       <!-- Modal Content für Abo-Modul -->
>       <div id="bankManageContentIframe">
>          {ST-Ajax}<WS-Ajax-bankManageContentIframe>{/ST-Ajax}
>
>             {SUBS-FixedPaymentCodeUsed(4)}
>                {SUBS-Bankdata}
>                   Ihre Abonnements werden mit folgender Bankverbindung bezahlt:
>                   ~WS-Fast_Include(Incl-Banks)~
>                   <a href="~BANK-EditLink~&otp3=updateBankSubscription">bearbeiten</a>
>                {/SUBS-Bankdata}
>
>                ~SUBS-BankSelection~
>                {BANK-BankSelection}
>                   {SUBS-Bankdata}
>                      Andere Bankverbindung wählen
>                   {/SUBS-Bankdata}
>
>                   {!SUBS-BankData}
>                      Sie haben noch keine Bankverbindung ausgewählt. Bitte wählen Sie eine Bankverbindung aus, damit sie Abonnements abschließen können.
>                   {/!SUBS-Bankdata}
>
>                   <form action="~FORM-Subscription~" id="subscriptionBank" method="POST">
>                      <select class="form-control" onchange="return ws_AJAXsendForm('subscriptionBank','~WS-Charset~',ws_AJAXSubscriptionStart(), ws_AJAXSubscriptionError(), 'ws_AJAXSubscriptionResponseSuccess()', 'ws_AJAXSubscriptionResponseError()')" name="~SUBS-BankIndex_input~">
>                         <option value="empty">Bitte wählen</option>
>                         {@BANK-Data}<option value="~BANK-BankSelection_code~" {DC-CompareDigitIBE($BANK-BankSelection_code$,$SUBS-BankIndex$,$BANK-BankSelection_code$)}selected{/DC-CompareDigitIBE($BANK-BankSelection_code$,$SUBS-BankIndex$,$BANK-BankSelection_code$)}>{BANK-UserDescr}~BANK-UserDescr~{/BANK-UserDescr} (~BANK-IBAN~, ~BANK-Bank~)</option>{/@BANK-Data}
>                      </select>
>                   </form>
>                {/BANK-BankSelection}
>
>                {!BANK-BankSelection}
>                   Sie haben in Ihrem Kundenkonto noch keine Bankverbindungen hinterlegt. Bitte hinterlegen Sie mindestens eine Bankverbindung, um ein Abonnement abzuschließen
>                {/!BANK-BankSelection}
>
>                {Bank-NewBankLink}
>                   <a href="~Bank-NewBankLink~&otp3=updateBankSubscription">Neue Bankverbindung anlegen</a>
>                {/Bank-NewBankLink}
>
>             {/SUBS-FixedPaymentCodeUsed(4)}
>
>             {BANK-Edit}
>                Bankverbindung bearbeiten
>                <form action="~FORM-BankManage~" method="POST" autocomplete="%%GlobalInputAutocompleteTxt%%" id="ws_bank_form">
>                   <input type="hidden" name="otp3" value="updateBankSubscription">
>
>                   {MSG-Bank_Notify}
>                      {BANK-SavedIndex}
>                         ~MSG-Bank_Notify~
>                      {/BANK-SavedIndex}
>                   {/MSG-Bank_Notify}
>
>                   {BANK-SEPADirectMandateType(2)}
>                      Für diese Bankverbindung liegt ein wiederkehrendes Mandat vor.<br>Die Daten dieser Bankverbindung sind daher nicht änderbar.<br>Legen Sie bitte bei Bedarf einfach eine neue Bankverbindung an.
>                   {/BANK-SEPADirectMandateType(2)}
>
>                   {!BANK-SEPADirectMandateType(2)}
>                      Bitte geben Sie in untenstehende Felder Ihre Bankdaten ein. <br>Felder, die mit einem "*" gekennzeichnet sind, sind erforderlich.
>                   {/!BANK-SEPADirectMandateType(2)}
>
>                   ~WS-Fast_Include(Incl-BankFieldsInputSEPA)~
>                   <div class="inputLabelFocus{BANK-UserDescr_error} is-invalid{/BANK-UserDescr_error}" data-placeholder="~BANK-UserDescr_text~">
>                      <label for="~BANK-UserDescr_input~" class="control-label">~BANK-UserDescr_text~</label>
>                      <input type="text" name="~BANK-UserDescr_input~" maxlength="80" value="~BANK-UserDescr~" class="form-control">
>                      {BANK-UserDescr_error}<span class="invalid-feedback">~BANK-UserDescr_errortext~</span>{/BANK-UserDescr_error}
>                   </div>
>
>                   {BANK-Mode_Manage}
>                      {!BANK-SEPADirectMandateType(2)}
>                         {BT-BankSubmit}
>                            <button type="submit" name="~BT-BankSubmit~">speichern</button>
>                         {/BT-BankSubmit}
>                      {/!BANK-SEPADirectMandateType(2)}
>                   {/BANK-Mode_Manage}
>                </form>
>             {/BANK-Edit}
>          {ST-Ajax}</WS-Ajax-bankManageContentIframe>{/ST-Ajax}
>       </div>
>
>       <script src="/$WS/ws_sysdata/js/ws-module-ajaxgeneric-1.8.5.js"></script>
>       <script>
>          function ws_AJAXSubscriptionResponseSuccess(){
>             ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~WS-BankManageLink~&otp3=updateBankSubscription', '~WS-Charset~', wsAjaxLoadBankStart, wsAjaxLoadBankError, 'wsAjaxLoadBankResponseSuccess()', 'wsAjaxLoadBankResponseError()');
>          }
>          function ws_AJAXSubscriptionStart(){}
>          function ws_AJAXSubscriptionError(){}
>          function ws_AJAXSubscriptionResponseError(){}
>          function ws_AJAXSubscriptionEditBankResponseSuccess(){
>             ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~WS-BankManageLink~&otp3=updateBankSubscription', '~WS-Charset~', wsAjaxLoadBankStart, wsAjaxLoadBankError, 'wsAjaxLoadBankResponseSuccess()', 'wsAjaxLoadBankResponseError()');
>          }
>          function ws_AJAXSubscriptionEditBankStart(){}
>          function ws_AJAXSubscriptionEditBankError(){}
>          function ws_AJAXSubscriptionEditBankResponseError(){}
>
>          function wsAjaxLoadBankStart(){}
>          function wsAjaxLoadBankError(){}
>          function wsAjaxLoadBankResponseSuccess(){}
>          function wsAjaxLoadBankResponseError(){}
>       </script>
>    {/WS-OTP3(updateBankSubscription)}
> </main>
> ...
> ```

> [!NOTE]
>
> Das Beispiel bindet `ws-module-ajaxgeneric-1.8.5.js` ein.
>
> `BANK-SEPADirectMandateType(2)` steht für ein wiederkehrendes SEPA-Mandat. Liegt eines vor, sind die Daten der Bankverbindung gesperrt und der Käufer legt stattdessen eine neue an.

## Schritt 5: Abo-Übersicht im Kundenkonto

`ws_subscription_manage.htm` ist ein neues Template, das es in älteren Shops noch nicht gibt. Es zeigt alle Abonnements des Kunden und bietet je Abo Lieferintervall, Menge und die drei Schaltflächen zum Löschen, Pausieren und Aktivieren an.

Lieferadresse und Bankverbindung stehen auch hier in Modal-Fenstern. Die beiden `ws_AJAXloadTemplate`-Aufrufe ganz am Ende füllen die Container beim Laden der Seite einmalig.

> [accordion] ws\_subscription\_manage.htm
>
> ```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
> ...
> <body>
>    <div id="subscriptionContent">
>       {ST-Ajax}<WS-Ajax-subscriptionContent>{/ST-Ajax}
>          Abonnements
>          <!-- Notifications -->
>          {MSG-Subscription_success}
>             {MSG-Subscription_success(1)}Ihr Abonnement wurde erfolgreich abgeschlossen.{/MSG-Subscription_success(1)}
>             {MSG-Subscription_success(2)}Ihr Abonnement wurde pausiert. Sie können das Abo jederzeit reaktivieren.{/MSG-Subscription_success(2)}
>             {MSG-Subscription_success(3)}Ihr Abonnement ist wieder aktiv. Das nächste Lieferdatum entnehmen Sie bitte der Übersicht.{/MSG-Subscription_success(3)}
>             {MSG-Subscription_success(4)}Ihr Abonnement wurde erfolgreich an Ihre Wünsche angepasst.{/MSG-Subscription_success(4)}
>             {MSG-Subscription_success(5)}Sie haben Ihr Abonnement gelöscht.{/MSG-Subscription_success(5)}
>             {MSG-Subscription_success(6)}Ihre erste Abo-Bestellung wird bald durchgeführt.{/MSG-Subscription_success(6)}
>             {MSG-Subscription_success(7)}Es wurde eine neue Bankverbindung für Ihre Abonnements festgelegt.{/MSG-Subscription_success(7)}
>          {/MSG-Subscription_success}
>
>          {MSG-Subscription_error}
>             {MSG-Subscription_error(1)}Sie sind nicht angemeldet. Um ein Abo abschließen zu können, müssen Sie angemeldet sein. Bitte loggen Sie sich mit Ihren Zugangsdaten ein. Sollten Sie noch keinen Kundenaccount haben, registrieren Sie sich bitte.{/MSG-Subscription_error(1)}
>             {MSG-Subscription_error(2)}Bitte prüfen Sie Ihre Eingaben. Die eingegebene Menge oder der Zeitraum Ihres Abos könnten fehlerhaft sein.{/MSG-Subscription_error(2)}
>             {MSG-Subscription_error(3)}Bitte reaktivieren Sie Ihr Abonnement, bevor Sie Änderungen daran vornehmen.{/MSG-Subscription_error(3)}
>             {MSG-Subscription_error(4)}Das ausgewählte Produkt ist kein Aboprodukt.{/MSG-Subscription_error(4)}
>             {MSG-Subscription_error(5)}Das ausgewählte Produkt ist kein Aboprodukt, da es aus einem Set von Produkten besteht.{/MSG-Subscription_error(5)}
>             {MSG-Subscription_error(6)}Bitte wählen Sie eine spezifische Produktvariante um das Abo abzuschließen.{/MSG-Subscription_error(6)}
>             {MSG-Subscription_error(7)}Das ausgewählte Produkt kann nicht gefunden werden.{/MSG-Subscription_error(7)}
>             {MSG-Subscription_error(8)}Ihre Abobestellung konnte nicht ausgeführt werden.{/MSG-Subscription_error(8)}
>          {/MSG-Subscription_error}
>
>          Sie haben ~SUBS-Data~ Abonnements. Hier können Sie Ihre Abonnements ändern, pausieren und löschen.
>
>          <!-- Adresse Details -->
>          Lieferadresse
>          Die Lieferung Ihrer Abonnements erfolgt an Ihre Rechnungsadresse:
>          {A-FirstName}
>             <div id="subscriptionAddress"></div>
>          {/A-FirstName}
>          <span data-toggle="modal" data-target="#modal-addressFields">Lieferadresse bearbeiten</span>
>
>          <!-- Bank Details -->
>          Ihre Bankverbindung
>          {SUBS-FixedPaymentCodeUsed(4)}
>             Ihre Abonnements werden mit folgender Bankverbindung bezahlt:
>             <div id="subscriptionBankOverview"></div>
>          {/SUBS-FixedPaymentCodeUsed(4)}
>          <span data-toggle="modal" data-target="#modal-bankFields">Bankverbindung bearbeiten</span>
>
>          <!-- Abonnierte Produkte -->
>          {!SUBS-Data}
>             Es befinden sich derzeit noch keine Produkte auf Ihrer Aboliste.
>          {/!SUBS-Data}
>
>          {SUBS-Data}
>             Hier finden Sie eine Übersicht Ihrer Abonnements.
>             <form action="~FORM-Subscription~" id="subscription" method="POST">
>                {@SUBS-Data}
>                   {SUBS-Data_state_ispaused}Abonnement pausiert{/SUBS-Data_state_ispaused}
>                   Produkt
>                   {PR-LoadData($SUBS-Data_PR_Number$,1)}
>                      {PR-LargeImage}
>                         <a href="~PR-DepVarLink~"><img src="~PR-LargeImage~" alt="~PR-Name_StripHtml~" /></a>
>                      {/PR-LargeImage}
>                   {/PR-LoadData($SUBS-Data_PR_Number$,1)}
>                   ~SUBS-Data_PR_Name~
>
>                   Lieferintervall
>                   <div class="inputLabelFocus" data-placeholder="Lieferintervall">
>                      <label for="~SUBS-CycleSelection_input~" class="control-label">Lieferintervall</label>
>                      <select class="form-control" name="~SUBS-CycleSelection_input~" onchange="return ws_AJAXsendForm('subscription','~WS-Charset~',ws_AJAXSubscriptionStart(), ws_AJAXSubscriptionError(), 'ws_AJAXSubscriptionResponseSuccess()', 'ws_AJAXSubscriptionResponseError()')">
>                         {@SUBS-CycleSelection_data}
>                            <option value="~SUBS-CycleSelection_data_value~" ~SUBS-CycleSelection_data_selected~>~SUBS-CycleSelection_data_descr~</option>
>                         {/@SUBS-CycleSelection_data}
>                      </select>
>                   </div>
>                   Erste Lieferung
>                   ~DC-DateTime_setunixtimestamp($SUBS-Data_nextorder_unixtimestamp$)~ ~DC-DateTime_day~. ~DC-DateTime_month~. ~DC-DateTime_year~
>
>                   Menge
>                   <div class="inputLabelFocus" data-placeholder="Menge">
>                      <label for="~SUBS-Data_PR_Quantity_input~" class="control-label">Menge</label>
>                      <input class="form-control" name="~SUBS-Data_PR_Quantity_input~" value="~SUBS-Data_PR_Quantity~" onblur="return ws_AJAXsendForm('subscription','~WS-Charset~',ws_AJAXSubscriptionStart(), ws_AJAXSubscriptionError(), 'ws_AJAXSubscriptionResponseSuccess()', 'ws_AJAXSubscriptionResponseError()')">
>                   </div>
>
>                   Gesamtpreis
>                   ~SUBS-Data_PR_Price~ ~WS-CurrencySymbol~ * ~SUBS-Data_PR_Quantity~
>                   {PR-LoadData($SUBS-Data_PR_Number$,1)}
>                      ~DC-FPCurrentPrice_set($PR-Price$)~
>                   {/PR-LoadData($SUBS-Data_PR_Number$,1)}
>                   {!DC-FPCurrentPrice($SUBS-Data_PR_Price$)}
>                      Preisänderung
>                      Aktueller Preis: ~DC-FPCurrentPrice~ ~WS-CurrencySymbol~
>                   {/!DC-FPCurrentPrice($SUBS-Data_PR_Price$)}
>
>                   {SUBS-Data_state_isactive}aktiv{/SUBS-Data_state_isactive}
>                   {SUBS-Data_state_isnew}aktiv{/SUBS-Data_state_isnew}
>                   {SUBS-Data_state_ispaused}pausiert{/SUBS-Data_state_ispaused}
>                   {SUBS-Data_state_istriggered}aktiv{/SUBS-Data_state_istriggered}
>
>                   <button type="submit" name="~SUBS-Data_Buttonname~" value="~SUBS-Data_Buttonvalue_delete~" onClick="return ws_AJAXsendForm('subscription','~WS-Charset~',ws_AJAXSubscriptionStart(), ws_AJAXSubscriptionError(), 'ws_AJAXSubscriptionResponseSuccess()', 'ws_AJAXSubscriptionResponseError()', '~SUBS-Data_Buttonname~=~SUBS-Data_Buttonvalue_delete~')">löschen</button>
>                   {SUBS-Data_Buttonvalue_pause}<button type="submit" name="~SUBS-Data_Buttonname~" value="~SUBS-Data_Buttonvalue_pause~" onClick="return ws_AJAXsendForm('subscription','~WS-Charset~',ws_AJAXSubscriptionStart(), ws_AJAXSubscriptionError(), 'ws_AJAXSubscriptionResponseSuccess()', 'ws_AJAXSubscriptionResponseError()', '~SUBS-Data_Buttonname~=~SUBS-Data_Buttonvalue_pause~')">pausieren</button>{/SUBS-Data_Buttonvalue_pause}
>                   {SUBS-Data_Buttonvalue_resume}<button type="submit" name="~SUBS-Data_Buttonname~" value="~SUBS-Data_Buttonvalue_resume~" onClick="return ws_AJAXsendForm('subscription','~WS-Charset~',ws_AJAXSubscriptionStart(), ws_AJAXSubscriptionError(), 'ws_AJAXSubscriptionResponseSuccess()', 'ws_AJAXSubscriptionResponseError()', '~SUBS-Data_Buttonname~=~SUBS-Data_Buttonvalue_resume~')">aktivieren</button>{/SUBS-Data_Buttonvalue_resume}
>                {/@SUBS-Data}
>             </form>
>          {/SUBS-Data}
>       {ST-Ajax}</WS-Ajax-subscriptionContent>{/ST-Ajax}
>    </div>
>
>    <!-- Adresse im Modal-Fenster ändern -->
>    <div class="modal fade" id="modal-addressFields" tabindex="-1" role="dialog">
>       <div class="modal-dialog modal-lg" role="document">
>          <div class="modal-content" id="modal-content-addressFields">
>             <div class="modal-body">
>                Adresse Bearbeiten
>                <button type="button" data-dismiss="modal" aria-label="Close" onclick="wsManualModalClose();"><span aria-hidden="true">&times;</span></button>
>                <div id="modal-body-addressFields"></div>
>             </div>
>          </div>
>       </div>
>    </div>
>
>    <!-- Bankdetails im Modal-Fenster ändern -->
>    <div class="modal fade" id="modal-bankFields" tabindex="-1" role="dialog">
>       <div class="modal-dialog modal-lg" role="document">
>          <div class="modal-content" id="modal-content-bankFields">
>             <div class="modal-body">
>                Bankdetails Bearbeiten
>                <button type="button" data-dismiss="modal" aria-label="Close" onclick="wsManualModalCloseBank();"><span aria-hidden="true">&times;</span></button>
>                <div id="modal-body-bankFields"></div>
>                <div id="bankManageIframe">
>                   <iframe src="~WS-EnsureNextLinkContainsURLParams~~WS-BankManageLink~&otp3=updateBankSubscription" frameborder="0" allowfullscreen class="iframe" style="height:605px;width:100%"></iframe>
>                </div>
>                <span onclick="wsManualModalCloseBank();">Fertig</span>
>             </div>
>          </div>
>       </div>
>    </div>
>
>    <!-- AJAX Funktionen -->
>    <script>
>       $(document).on("click","#modal-addressFields",function(e){
>          if(!$(e.target).is("#modal-addressFields *")) {
>             wsManualModalClose();
>          }
>       })
>
>       $(document).on("click","#modal-bankFields",function(e){
>          if(!$(e.target).is("#modal-bankFields *")) {
>             wsManualModalCloseBank();
>          }
>       })
>
>       function wsManualModalClose(){
>          $("#modal-addressFields").modal("hide");
>       }
>
>       function wsManualModalCloseBank(){
>          $("#modal-bankFields").modal("hide");
>          ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~WS-BankManageLink~&otp3=updateBankSubscription', '~WS-Charset~', wsAjaxLoadBankStart, wsAjaxLoadBankError, 'wsAjaxLoadBankResponseSuccess()', 'wsAjaxLoadBankResponseError()');
>       }
>
>       function wsAjaxLoadAdrressStart(){}
>       function wsAjaxLoadAdrressError(){}
>       function wsAjaxLoadAdrressResponseSuccess(){}
>       function wsAjaxLoadAdrressResponseError(){}
>       function wsAjaxLoadAdrressSaveResponseSuccess(){}
>       ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~WS-AddrManageLink~&otp3=updateAddressSubscription', '~WS-Charset~', wsAjaxLoadAdrressStart, wsAjaxLoadAdrressError, 'wsAjaxLoadAdrressResponseSuccess()', 'wsAjaxLoadAdrressResponseError()');
>
>       function wsAjaxLoadBankStart(){}
>       function wsAjaxLoadBankError(){}
>       function wsAjaxLoadBankResponseSuccess(){}
>       function wsAjaxLoadBankResponseError(){}
>       ws_AJAXloadTemplate('~WS-EnsureNextLinkContainsURLParams~~WS-BankManageLink~&otp3=updateBankSubscription', '~WS-Charset~', wsAjaxLoadBankStart, wsAjaxLoadBankError, 'wsAjaxLoadBankResponseSuccess()', 'wsAjaxLoadBankResponseError()');
>    </script>
> </body>
> ...
> ```

Die Preisprüfung im Listing ist ein nützliches Detail: `PR-LoadData` holt den heutigen Produktpreis, `DC-FPCurrentPrice_set` merkt ihn, und der Vergleich mit `SUBS-Data_PR_Price` zeigt dem Kunden eine Preisänderung gegenüber dem Preis bei Abo-Abschluss an.

## Die Meldungscodes im Überblick

Das Listing aus Schritt 5 zeigt die Erfolgs- und Fehlermeldungen als nummerierte Abschnitte.

`~MSG-Subscription_success~` und `~MSG-Subscription_error~` geben als einzelnes Tag den aktuellen Code aus. Als Abschnitt ohne Parameter sind sie wahr, sobald überhaupt ein Code gesetzt ist. Mit Parameter prüfen sie auf eine bestimmte Nummer.

| Erfolgscode | Wird gesetzt, wenn |
| - | - |
| 1 | ein Abonnement neu abgeschlossen wurde |
| 2 | ein Abonnement pausiert wurde |
| 3 | ein pausiertes Abonnement reaktiviert wurde |
| 4 | Menge, Lieferintervall, Zahlungsart oder Lieferadresse geändert wurden |
| 5 | ein Abonnement gelöscht wurde |
| 6 | die noch nicht ausgelösten Abonnements angestoßen wurden |
| 7 | nur die allgemeinen Abo-Daten geändert wurden, etwa die Bankverbindung |

| Fehlercode | Bedeutung |
| - | - |
| 1 | der Kunde ist nicht angemeldet |
| 2 | Menge oder Lieferintervall sind ungültig |
| 3 | das Abonnement ist pausiert und muss erst reaktiviert werden |
| 4 | das Produkt ist kein Abo-Produkt |
| 5 | das Produkt ist ein Set-Produkt, Sets werden nicht unterstützt |
| 6 | ein abhängiges Variantenprodukt wurde nicht bis zur konkreten Variante aufgelöst |
| 7 | die Produktnummer existiert nicht |
| 8 | es gibt keine Abonnements, die ausgelöst werden könnten |
| 9 | die Zahlungsart steht nicht in `AllowedPayments` |
| 10 | Lastschrift ohne ausgewählte Bankverbindung |
| 11 | die maximale Anzahl an Abonnements ist erreicht |

Die Codes 9, 10 und 11 fehlen im Beispiel-Listing. Wenn Sie es übernehmen, ergänzen Sie für diese drei eine Meldung, sonst bleibt die Seite im Fehlerfall stumm.

> [!WARNING]
>
> Oberhalb von 900 liegen technische Fehler, die der Kunde nicht beheben kann: 901 steht für ein nicht freigeschaltetes Feature, 902 bis 912 für Datenbankfehler. Codes ab 950 und ab 1000 entstehen, wenn der Shop einen Fehler nicht zuordnen kann, und werden zusätzlich als kritischer Fehler protokolliert.
>
> Fangen Sie diese Fälle mit `{MSG-Subscription_error}` ohne Parameter ab und zeigen Sie eine allgemeine Meldung an, damit der Kunde nicht vor einer leeren Seite steht.

## Die Zustände eines Abonnements

`~SUBS-Data_state_code~` gibt den Zustand als Zahl aus, die vier Abschnitts-Tags fragen ihn einzeln ab.

| Tag | Zustand |
| - | - |
| `SUBS-Data_state_isactive` | aktiv, laufendes Abonnement |
| `SUBS-Data_state_isnew` | neu, die erste Bestellung wurde noch nicht ausgelöst |
| `SUBS-Data_state_ispaused` | pausiert |
| `SUBS-Data_state_istriggered` | die Bestellung wurde angestoßen |

Das Beispiel-Listing beschriftet `SUBS-Data_state_isactive`, `SUBS-Data_state_isnew` und `SUBS-Data_state_istriggered` alle drei mit "aktiv". Für die reine Anzeige ist das in Ordnung, es verdeckt aber einen Unterschied: nur bei neuen Abonnements bietet `SUBS-Buttonvalue_triggernewsubscriptions` die Schaltfläche an, mit der der Kunde die erste Bestellung selbst auslöst.


## Related topics

- [Abo-Modul](https://dokumentation.websale.de/v8s/marketing-funktionen/abo-modul.md)
- [E-SUBS- (Abo-Modul)](https://dokumentation.websale.de/v8s/referenz/tags/e-subs-abo-modul.md)
- [Spezielle Online-Dienste](https://dokumentation.websale.de/v8s/online-servicebereich/spezielle-online-dienste.md)
- [PayPal Checkout](https://dokumentation.websale.de/v8s/zahlungsarten-anlegen/paypal-checkout.md)
- [MSG- (Meldungen)](https://dokumentation.websale.de/v8s/referenz/tags/msg-meldungen.md)


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