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

# Materials: subscription module

> Complete template examples for the subscription module: subscription checkout without page changes, with modal windows, AJAX, and dependent variants.

This page contains detailed template examples for the subscription module. They supplement the instructions with a complete implementation that you can adopt as a starting point.

The solution shown avoids redirects to `ws_bank_manage.htm` and `ws_address_manage.htm`. The buyer stays on `ws_product.htm` or `ws_subscription_manage.htm` and edits the address and bank details in modal windows. Dependent variants are taken into account.

> [!NOTE]
>
> The modal windows are implemented with Bootstrap 4. The functions also work with other Bootstrap versions.
>
> The examples require jQuery and the WEBSALE function `ws_AJAXloadTemplate`. The latter is included in the module for reloading templates or in the system or core library.

Guide: Subscription module · Reference: SUBS tags, WEBSALE JavaScript library

> [!WARNING]
>
> The following listings are excerpts. The three dots at the beginning and end stand for the remaining content of the respective template, which stays unchanged.
>
> All labels and message texts are sample texts. Replace them with your own or with text placeholders from the `shop.config`.

## Overview of the five templates

| Step | Template | Purpose |
| - | - | - |
| 1 | `ws_product.htm` | Subscription product with delivery interval, address, bank details, and the three modal windows |
| 2 | `ws_login.htm` | Login and registration in the modal window |
| 3 | `ws_address_manage.htm` | Edit the billing address in the modal window |
| 4 | `ws_bank_manage.htm` | Edit the bank details in the modal window |
| 5 | `ws_subscription_manage.htm` | New subscription overview in the customer account |

The order isn't a recommendation but a dependency: step 1 loads the templates from steps 2 to 4 via AJAX. So build step 1 first and add the others afterward.

## Step 1: Product detail view

This template carries the main load. It displays the subscription product, uses `DC-FPAboOrderOk` to check whether all requirements are met, and provides the three modal windows for login, address, and bank details.

> [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]
>
> The `DC-FPAboOrderOk` flag is the core of the logic. Three checks set it to `false`: missing billing address, missing bank details, and missing interval selection. Only when all three pass is the checkout button no longer disabled.
>
> If you have additional requirements, simply add another line following the same pattern.

## Step 2: Login in the modal window

`ws_login.htm` provides the login and registration fields that step 1 loads into the modal window via AJAX. The entire subscription part is contained in the one-time parameter `WS-OTP3(loginSubscription)`. The negated section above it ensures that the regular page content isn't delivered along with it when the template is requested as an AJAX fragment.

Within it, `WS-OTP1(NewUserSubscription)` distinguishes between the login of an existing customer and a new registration. Both forms reference each other via `ws_AJAXloadTemplate`, so the buyer can switch between the two views in the modal window without leaving the product page.

> [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]
>
> The three `DC-SPRegisterParams…_set` lines store the one-time parameters in session parameters. This is the only way the buyer can find their way back into the subscription context after registering or after a confirmation email.
>
> The links also carry `otpsubscriptionprodindex` and, for dependent variants, `otpsubscriptionvarindex`. This way, the modal window still knows which product was involved after switching between login and registration.

## Step 3: Billing address in the modal window

The subscription module needs `ws_address_manage.htm` in two places. The upper block in `ST-Ajax` provides the plain address display that step 1 and step 5 load into their `subscriptionAddress` container. The lower block provides the address form for the modal window.

Again, a one-time parameter separates the two worlds, this time `WS-OTP3(updateAddressSubscription)`. The page's normal address form is kept in a negated section so that the customer account continues to work as usual.

> [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]
>
> Both forms carry the same ID `addressform`. This only works because the one-time parameter ensures that the two blocks are never output at the same time. If you remove the negated section, the ID appears twice in the document and saving in the modal window accesses the wrong form.

## Step 4: Bank details in the modal window

`ws_bank_manage.htm` is the most extensive of the four reloaded templates because it has to cover three states: displaying an already selected bank account, selecting from the existing bank accounts, and creating or editing a bank account.

The entire selection depends on `SUBS-FixedPaymentCodeUsed(4)`. The value 4 is the payment method code for direct debit. The module uses it to check whether a fixed payment method for subscriptions is set in the `shop.config` and whether it is direct debit. Only then are bank details needed at all.

> [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]
>
> The example includes `ws-module-ajaxgeneric-1.8.5.js`. This module is missing from the JavaScript library overview and is added here.
>
> `BANK-SEPADirectMandateType(2)` stands for a recurring SEPA mandate. If one exists, the bank details are locked and the buyer creates a new bank account instead.

## Step 5: Subscription overview in the customer account

`ws_subscription_manage.htm` is a new template that doesn't exist in older shops yet. It shows all of the customer's subscriptions and offers, for each subscription, the delivery interval, quantity, and the three buttons for deleting, pausing, and activating.

Here too, the delivery address and bank details are shown in modal windows. The two `ws_AJAXloadTemplate` calls at the very end fill the containers once when the page loads.

> [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>
> ...
> ```

> [!NOTE]
>
> The price check in the listing is a useful detail: `PR-LoadData` fetches today's product price, `DC-FPCurrentPrice_set` stores it, and the comparison with `SUBS-Data_PR_Price` shows the customer a price change compared to the price at the time the subscription was taken out.

## Overview of the message codes

The listing from step 5 shows the success and error messages as numbered sections. The previous documentation doesn't state which number is set when. The following tables have been added here.

As a single tag, `~MSG-Subscription_success~` and `~MSG-Subscription_error~` output the current code. As a section without a parameter, they are true as soon as any code is set. With a parameter, they check for a specific number.

| Success code | Set when |
| - | - |
| 1 | a new subscription was taken out |
| 2 | a subscription was paused |
| 3 | a paused subscription was reactivated |
| 4 | the quantity, delivery interval, payment method, or delivery address was changed |
| 5 | a subscription was deleted |
| 6 | the subscriptions not yet triggered were initiated |
| 7 | only the general subscription data was changed, for example the bank details |

| Error code | Meaning |
| - | - |
| 1 | the customer isn't logged in |
| 2 | the quantity or delivery interval is invalid |
| 3 | the subscription is paused and must be reactivated first |
| 4 | the product isn't a subscription product |
| 5 | the product is a set product; sets aren't supported |
| 6 | a dependent variant product wasn't resolved down to the specific variant |
| 7 | the product number doesn't exist |
| 8 | there are no subscriptions that could be triggered |
| 9 | the payment method isn't listed in `AllowedPayments` |
| 10 | direct debit without a selected bank account |
| 11 | the maximum number of subscriptions has been reached |

Codes 9, 10, and 11 are missing from the sample listing. If you adopt it, add a message for these three; otherwise the page stays silent in case of an error.

> [!WARNING]
>
> Codes above 900 are technical errors that the customer can't fix: 901 stands for a feature that isn't enabled, 902 to 912 for database errors. Codes from 950 and from 1000 occur when the shop can't classify an error, and they are additionally logged as a critical error.
>
> Catch these cases with `{MSG-Subscription_error}` without a parameter and display a general message so that the customer isn't faced with an empty page.

## Subscription states

`~SUBS-Data_state_code~` outputs the state as a number; the four section tags query it individually. This has also been added here.

| Tag | State |
| - | - |
| `SUBS-Data_state_isactive` | active, running subscription |
| `SUBS-Data_state_isnew` | new, the first order hasn't been triggered yet |
| `SUBS-Data_state_ispaused` | paused |
| `SUBS-Data_state_istriggered` | the order has been initiated |

> [!NOTE]
>
> The sample listing labels `SUBS-Data_state_isactive`, `SUBS-Data_state_isnew`, and `SUBS-Data_state_istriggered` all three as "aktiv" (active). That's fine for display purposes, but it hides a difference: only for new subscriptions does `SUBS-Buttonvalue_triggernewsubscriptions` offer the button the customer can use to trigger the first order themselves.

> [!NOTE]
>
> Compared to the previous documentation, nine places in the listings have been corrected: two typos in sample texts, a `span` with `href` that doesn't work as a link, the outside click on the bank modal (twice), which previously closed the address modal, and four instances of accessing the global `event` instead of the click function's parameter.


## Related topics

- [E-SUBS- (Subscription module)](https://dokumentation.websale.de/en/v8s/referenz/tags/e-subs-abo-modul.md)
- [Materials: BestPrice order module](https://dokumentation.websale.de/en/v8s/materialien/materialien-bestprice-order-modul.md)
- [Special online services](https://dokumentation.websale.de/en/v8s/online-servicebereich/spezielle-online-dienste.md)
- [MSG- (Messages)](https://dokumentation.websale.de/en/v8s/referenz/tags/msg-meldungen.md)
- [SUBS- (managing subscriptions)](https://dokumentation.websale.de/en/v8s/referenz/tags/subs/abos-verwalten.md)


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