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

# Full control

> Use <resurs-payment-method-group> for complete layout and state flexibility.

export const PaymentMethodIcon = ({type}) => {
  const containerRef = useRef(null);
  useEffect(() => {
    if (containerRef.current && !containerRef.current.querySelector('resurs-payment-method-icon')) {
      const icon = document.createElement('resurs-payment-method-icon');
      icon.setAttribute('type', type);
      containerRef.current.appendChild(icon);
    }
    return () => {
      if (containerRef.current) {
        containerRef.current.innerHTML = '';
      }
    };
  }, [type]);
  return <span ref={containerRef} style={{
    display: 'inline-block'
  }} />;
};

export const PaymentMethod = ({methods}) => {
  const containerRef = useRef(null);
  useEffect(() => {
    if (!containerRef.current || !methods || containerRef.current.querySelector('.custom-tabs')) return;
    const style = document.createElement('style');
    style.textContent = `
      .custom-tabs { font-family: system-ui, sans-serif; border: 1px solid light-dark(#e0e0e0, #333); border-radius: 12px; overflow: hidden; }
      .custom-tabs-bar { display: flex; background: light-dark(#fafafa, #1a1e21); border-bottom: 1px solid light-dark(#e0e0e0, #333); overflow-x: auto; }
      .custom-tab {
        flex: 1; padding: 14px 16px; border: none; background: none; cursor: pointer;
        font-size: 14px; font-weight: 500; color: light-dark(#666, #999); white-space: nowrap;
        border-bottom: 2px solid transparent; transition: all 0.15s;
      }
      .custom-tab:not(:last-child) { border-right: 1px solid light-dark(#e0e0e0, #333); }
      .custom-tab:hover { color: light-dark(#333, #ccc); background: light-dark(#f5f5f5, #252a2e); }
      .custom-tab[aria-selected="true"] { color: light-dark(#006965, #26938f); border-bottom-color: light-dark(#006965, #26938f); background: light-dark(#fff, #212529); }
      .custom-tabs-content { position: relative; background: light-dark(#fff, #212529); }
      .custom-tab-panel { position: absolute; top: 0; left: 0; width: 100%; padding: 16px 20px; visibility: hidden; opacity: 0; transition: opacity 0.2s ease; }
      .custom-tab-panel.active { position: relative; visibility: visible; opacity: 1; }
    `;
    containerRef.current.appendChild(style);
    const tabs = document.createElement('div');
    tabs.className = 'custom-tabs';
    const bar = document.createElement('div');
    bar.className = 'custom-tabs-bar';
    bar.setAttribute('role', 'tablist');
    tabs.appendChild(bar);
    const content = document.createElement('div');
    content.className = 'custom-tabs-content';
    tabs.appendChild(content);
    const tabButtons = [];
    const panels = [];
    const widgets = [];
    methods.forEach((group, i) => {
      const tab = document.createElement('button');
      tab.className = 'custom-tab';
      tab.setAttribute('role', 'tab');
      tab.setAttribute('aria-selected', 'false');
      tab.textContent = group.title;
      bar.appendChild(tab);
      const panel = document.createElement('div');
      panel.className = 'custom-tab-panel';
      panel.setAttribute('role', 'tabpanel');
      const widget = document.createElement('resurs-payment-method-group');
      widget.data = group.internalData;
      panel.appendChild(widget);
      content.appendChild(panel);
      tabButtons.push(tab);
      panels.push(panel);
      widgets.push(widget);
      tab.addEventListener('click', () => {
        tabButtons.forEach(t => t.setAttribute('aria-selected', 'false'));
        panels.forEach(p => p.classList.remove('active'));
        widgets.forEach(w => {
          w.active = false;
        });
        tab.setAttribute('aria-selected', 'true');
        panel.classList.add('active');
        widget.active = true;
      });
    });
    if (tabButtons.length > 0) {
      tabButtons[0].setAttribute('aria-selected', 'true');
      panels[0].classList.add('active');
      widgets[0].active = true;
    }
    tabs.addEventListener('resursPaymentMethodSelected', e => {
      e.stopPropagation();
      const active = tabs.querySelector('resurs-payment-method-group[active]');
      if (active) active.selectedId = e.detail.id;
    });
    tabs.addEventListener('resursPaymentMethodDeselected', e => {
      e.stopPropagation();
    });
    containerRef.current.appendChild(tabs);
    return () => {
      if (containerRef.current) {
        containerRef.current.innerHTML = '';
      }
    };
  }, [methods]);
  return <div ref={containerRef} />;
};

export const paymentSelectorMockData = [{
  "title": "Invoice",
  "subtitle": "Pay only after your item is delivered",
  "type": "RESURS_INVOICE",
  "customerTypes": ["NATURAL"],
  "internalData": "eyJtZXRob2RJZHMiOlsiZTA3ZDdkZDktYzZkMy00OTRkLWIxMzYtNTZmY2M1ODMxYWIwIl0sInR5cGUiOiJSRVNVUlNfSU5WT0lDRSIsImN1c3RvbWVyVHlwZXMiOlsiTkFUVVJBTCJdLCJtZXRob2RPcHRpb25zIjpbeyJtZXRob2RJZCI6ImUwN2Q3ZGQ5LWM2ZDMtNDk0ZC1iMTM2LTU2ZmNjNTgzMWFiMCIsInNlbGVjdGFibGUiOmZhbHNlLCJzZWxlY3RlZCI6ZmFsc2UsInByb3BlcnRpZXMiOnsiZGVzY3JpcHRpb24iOnsiZGVzY3JpcHRpb25UZXh0IjpbIk5vIGludm9pY2UgZmVlIiwiQXQgbGVhc3QgMSBtb250aCB0byBwYXkgYWZ0ZXIgZGVsaXZlcnkiXSwiZGVzY3JpcHRpb25Gb3JtYXQiOiJ2ZXJ0aWNhbC1saXN0In19LCJvdmVycmlkZXMiOnsibGlua3MiOlt7ImxhYmVsIjoiRnVsbCB0ZXJtcyBhbmQgY29uZGl0aW9ucyIsInR5cGUiOiJTRUtLSSIsInVybCI6IiMifV19fV0sInByb3BlcnRpZXMiOnsidGl0bGUiOiJJbnZvaWNlIiwic3VidGl0bGUiOiJQYXkgb25seSBhZnRlciB5b3VyIGl0ZW0gaXMgZGVsaXZlcmVkIn19"
}, {
  "title": "Pay at your own pace",
  "subtitle": "Pay interest-free from 456.00 kr per month",
  "type": "RESURS_PART_PAYMENT_SELECT_NOW",
  "customerTypes": ["NATURAL"],
  "internalData": "ewogICJtZXRob2RJZHMiOiBbCiAgICAiMDdkZmQ3YjQtNDJlOS00NzJhLWJhZGQtN2E4M2YzMjMyMGVmIiwKICAgICIyYWE0MzZmNC00NjcxLTQ1NDYtODliNS1jNWIxYmFmZWE3NGUiLAogICAgIjEzNzAwOTdkLWY1YWUtNDkzYi04ZDY0LTgzNTRlOWZhZWQ0MSIKICBdLAogICJ0eXBlIjogIlJFU1VSU19QQVJUX1BBWU1FTlRfU0VMRUNUX05PVyIsCiAgInNob3dDcmVkaXRXYXJuaW5nIjogdHJ1ZSwKICAiY3VzdG9tZXJUeXBlcyI6IFsKICAgICJOQVRVUkFMIgogIF0sCiAgIm1ldGhvZE9wdGlvbnMiOiBbCiAgICB7CiAgICAgICJtZXRob2RJZCI6ICIwN2RmZDdiNC00MmU5LTQ3MmEtYmFkZC03YTgzZjMyMzIwZWYiLAogICAgICAic2VsZWN0YWJsZSI6IHRydWUsCiAgICAgICJzZWxlY3RlZCI6IGZhbHNlLAogICAgICAicHJvcGVydGllcyI6IHsKICAgICAgICAidGl0bGUiOiAiNiBNb250aHMgSW50ZXJlc3QgRnJlZSIsCiAgICAgICAgInN1YnRpdGxlIjogIjg3My4wMCBrciBwZXIgbW9udGgiLAogICAgICAgICJkZXNjcmlwdGlvbiI6IHsKICAgICAgICAgICJkZXNjcmlwdGlvblRleHQiOiBbCiAgICAgICAgICAgICJWYXJpYWJsZSBpbnRlcmVzdCByYXRlIDAuMDAlLiBFZmZlY3RpdmUgaW50ZXJlc3QgcmF0ZSAzNy40OSUuIFRvdGFsIGZlZXMgNTI5LjAwIGtyLiIsCiAgICAgICAgICAgICIqKlRvdGFsIHRvIHJlcGF5IDUsNTI5LjAwIGtyKioiCiAgICAgICAgICBdLAogICAgICAgICAgImRlc2NyaXB0aW9uRm9ybWF0IjogImlubGluZSIKICAgICAgICB9CiAgICAgIH0sCiAgICAgICJvdmVycmlkZXMiOiB7CiAgICAgICAgImxpbmtzIjogWwogICAgICAgICAgewogICAgICAgICAgICAibGFiZWwiOiAiRnVsbCB0ZXJtcyBhbmQgY29uZGl0aW9ucyIsCiAgICAgICAgICAgICJ0eXBlIjogIlBSSUNFX0lORk8iLAogICAgICAgICAgICAidXJsIjogIiMiCiAgICAgICAgICB9CiAgICAgICAgXQogICAgICB9LAogICAgICAiYmFzZUFjY291bnREZXNjcmlwdGlvbiI6ICJXaXRoIGVhY2ggcGF5bWVudCwgeW91IG11c3QgY29uZmlybSB5b3VyIGNob2ljZSBvZiBwYXltZW50IHBsYW4uIElmIHRoZSB0ZXJtcyBvZiB0aGUgc2VsZWN0ZWQgcGF5bWVudCBwbGFuIGFyZSBub3QgZm9sbG93ZWQsIHRoZSB0ZXJtcyBvZiB0aGUgbG9uZ2VzdCBwYXltZW50IHBsYW4gYWNjb3JkaW5nIHRvIHRoZSBhZ3JlZW1lbnQgd2lsbCBhcHBseS4gVmFyaWFibGUgaW50ZXJlc3QgcmF0ZSAxLjUgJS4gVG90YWwgZmVlcyAxNSwzNDgga3IuIEVmZmVjdGl2ZSBpbnRlcmVzdCByYXRlIDIuMyAlLiBNYXR1cml0eSBbMS8xMl0gaW5zdGFsbG1lbnQgMzQ4IGtyL21vbnRoLiBUb3RhbDogNTgsNzAwIGtyLiIKICAgIH0sCiAgICB7CiAgICAgICJtZXRob2RJZCI6ICIyYWE0MzZmNC00NjcxLTQ1NDYtODliNS1jNWIxYmFmZWE3NGUiLAogICAgICAic2VsZWN0YWJsZSI6IHRydWUsCiAgICAgICJzZWxlY3RlZCI6IGZhbHNlLAogICAgICAicHJvcGVydGllcyI6IHsKICAgICAgICAidGl0bGUiOiAiMTAgTW9udGhzIEludGVyZXN0IEZyZWUgQ0FNUEFJR04iLAogICAgICAgICJzdWJ0aXRsZSI6ICI1NDkuMDAga3IgcGVyIG1vbnRoIiwKICAgICAgICAiZGVzY3JpcHRpb24iOiB7CiAgICAgICAgICAiZGVzY3JpcHRpb25UZXh0IjogWwogICAgICAgICAgICAiVmFyaWFibGUgaW50ZXJlc3QgcmF0ZSAwLjAwJS4gRWZmZWN0aXZlIGludGVyZXN0IHJhdGUgMjAuOTQlLiBUb3RhbCBmZWVzIDQ5MC4wMCBrci4iLAogICAgICAgICAgICAiKipUb3RhbCB0byByZXBheSA1LDQ5MC4wMCBrcioqIgogICAgICAgICAgXSwKICAgICAgICAgICJkZXNjcmlwdGlvbkZvcm1hdCI6ICJpbmxpbmUiCiAgICAgICAgfQogICAgICB9LAogICAgICAib3ZlcnJpZGVzIjogewogICAgICAgICJsaW5rcyI6IFsKICAgICAgICAgIHsKICAgICAgICAgICAgImxhYmVsIjogIkZ1bGwgdGVybXMgYW5kIGNvbmRpdGlvbnMiLAogICAgICAgICAgICAidHlwZSI6ICJQUklDRV9JTkZPIiwKICAgICAgICAgICAgInVybCI6ICIjIgogICAgICAgICAgfQogICAgICAgIF0KICAgICAgfSwKICAgICAgImJhc2VBY2NvdW50RGVzY3JpcHRpb24iOiAiV2l0aCBlYWNoIHBheW1lbnQsIHlvdSBtdXN0IGNvbmZpcm0geW91ciBjaG9pY2Ugb2YgcGF5bWVudCBwbGFuLiBJZiB0aGUgdGVybXMgb2YgdGhlIHNlbGVjdGVkIHBheW1lbnQgcGxhbiBhcmUgbm90IGZvbGxvd2VkLCB0aGUgdGVybXMgb2YgdGhlIGxvbmdlc3QgcGF5bWVudCBwbGFuIGFjY29yZGluZyB0byB0aGUgYWdyZWVtZW50IHdpbGwgYXBwbHkuIFZhcmlhYmxlIGludGVyZXN0IHJhdGUgMS41ICUuIFRvdGFsIGZlZXMgMTUsMzQ4IGtyLiBFZmZlY3RpdmUgaW50ZXJlc3QgcmF0ZSAyLjMgJS4gTWF0dXJpdHkgWzEvMTJdIGluc3RhbGxtZW50IDM0OCBrci9tb250aC4gVG90YWw6IDU4LDcwMCBrci4iCiAgICB9LAogICAgewogICAgICAibWV0aG9kSWQiOiAiMTM3MDA5N2QtZjVhZS00OTNiLThkNjQtODM1NGU5ZmFlZDQxIiwKICAgICAgInNlbGVjdGFibGUiOiB0cnVlLAogICAgICAic2VsZWN0ZWQiOiB0cnVlLAogICAgICAicHJvcGVydGllcyI6IHsKICAgICAgICAidGl0bGUiOiAiMTIgTW9udGhzIEludGVyZXN0IEZyZWUiLAogICAgICAgICJzdWJ0aXRsZSI6ICI0NTYuMDAga3IgcGVyIG1vbnRoIiwKICAgICAgICAiZGVzY3JpcHRpb24iOiB7CiAgICAgICAgICAiZGVzY3JpcHRpb25UZXh0IjogWwogICAgICAgICAgICAiVmFyaWFibGUgaW50ZXJlc3QgcmF0ZSAwLjAwJS4gRWZmZWN0aXZlIGludGVyZXN0IHJhdGUgMzQuNTklLiBUb3RhbCBmZWVzIDg2My4wMCBrci4iLAogICAgICAgICAgICAiKipUb3RhbCB0byByZXBheSA1LDg2My4wMCBrcioqIgogICAgICAgICAgXSwKICAgICAgICAgICJkZXNjcmlwdGlvbkZvcm1hdCI6ICJpbmxpbmUiCiAgICAgICAgfQogICAgICB9LAogICAgICAib3ZlcnJpZGVzIjogewogICAgICAgICJsaW5rcyI6IFsKICAgICAgICAgIHsKICAgICAgICAgICAgImxhYmVsIjogIkZ1bGwgdGVybXMgYW5kIGNvbmRpdGlvbnMiLAogICAgICAgICAgICAidHlwZSI6ICJQUklDRV9JTkZPIiwKICAgICAgICAgICAgInVybCI6ICIjIgogICAgICAgICAgfQogICAgICAgIF0KICAgICAgfSwKICAgICAgImJhc2VBY2NvdW50RGVzY3JpcHRpb24iOiAiV2l0aCBlYWNoIHBheW1lbnQsIHlvdSBtdXN0IGNvbmZpcm0geW91ciBjaG9pY2Ugb2YgcGF5bWVudCBwbGFuLiBJZiB0aGUgdGVybXMgb2YgdGhlIHNlbGVjdGVkIHBheW1lbnQgcGxhbiBhcmUgbm90IGZvbGxvd2VkLCB0aGUgdGVybXMgb2YgdGhlIGxvbmdlc3QgcGF5bWVudCBwbGFuIGFjY29yZGluZyB0byB0aGUgYWdyZWVtZW50IHdpbGwgYXBwbHkuIFZhcmlhYmxlIGludGVyZXN0IHJhdGUgMS41ICUuIFRvdGFsIGZlZXMgMTUsMzQ4IGtyLiBFZmZlY3RpdmUgaW50ZXJlc3QgcmF0ZSAyLjMgJS4gTWF0dXJpdHkgWzEvMTJdIGluc3RhbGxtZW50IDM0OCBrci9tb250aC4gVG90YWw6IDU4LDcwMCBrci4iCiAgICB9CiAgXSwKICAicHJvcGVydGllcyI6IHsKICAgICJ0aXRsZSI6ICJQYXkgYXQgeW91ciBvd24gcGFjZSIsCiAgICAic3VidGl0bGUiOiAiUGF5IGludGVyZXN0LWZyZWUgZnJvbSA0NTYuMDAga3IgcGVyIG1vbnRoIgogIH0KfQ=="
}, {
  "title": "Resurs Card",
  "subtitle": "Pay with your Resurs card",
  "type": "RESURS_CARD",
  "customerTypes": ["NATURAL"],
  "internalData": "eyJtZXRob2RJZHMiOlsiYTNjMWY5MjAtOGI3NC00ZTVhLTlkMDItMWY2ZTgzYTQ3YjVjIl0sInR5cGUiOiJSRVNVUlNfQ0FSRCIsInNob3dDcmVkaXRXYXJuaW5nIjp0cnVlLCJjdXN0b21lclR5cGVzIjpbIk5BVFVSQUwiXSwibWV0aG9kT3B0aW9ucyI6W3sibWV0aG9kSWQiOiJhM2MxZjkyMC04Yjc0LTRlNWEtOWQwMi0xZjZlODNhNDdiNWMiLCJzZWxlY3RhYmxlIjpmYWxzZSwic2VsZWN0ZWQiOmZhbHNlLCJwcm9wZXJ0aWVzIjp7ImRlc2NyaXB0aW9uIjp7ImRlc2NyaXB0aW9uVGV4dCI6WyJQYXkgd2l0aCB5b3VyIFJlc3VycyBjYXJkIiwiVXNlIHlvdXIgZXhpc3RpbmcgY3JlZGl0Il0sImRlc2NyaXB0aW9uRm9ybWF0IjoidmVydGljYWwtbGlzdCJ9fSwib3ZlcnJpZGVzIjp7ImxpbmtzIjpbeyJsYWJlbCI6IkZ1bGwgdGVybXMgYW5kIGNvbmRpdGlvbnMiLCJ0eXBlIjoiU0VLS0kiLCJ1cmwiOiIjIn1dfX1dLCJwcm9wZXJ0aWVzIjp7InRpdGxlIjoiUmVzdXJzIENhcmQiLCJzdWJ0aXRsZSI6IlBheSB3aXRoIHlvdXIgUmVzdXJzIGNhcmQifX0="
}, {
  "title": "Revolving Credit",
  "subtitle": "Flexible payments with revolving credit",
  "type": "RESURS_REVOLVING_CREDIT",
  "customerTypes": ["NATURAL"],
  "internalData": "eyJtZXRob2RJZHMiOlsiZDRlMmExYjMtN2M4OS00ZjZkLWE1MjMtOWU4YjFjNGQ3ZjBhIl0sInR5cGUiOiJSRVNVUlNfUkVWT0xWSU5HX0NSRURJVCIsInNob3dDcmVkaXRXYXJuaW5nIjp0cnVlLCJjdXN0b21lclR5cGVzIjpbIk5BVFVSQUwiXSwibWV0aG9kT3B0aW9ucyI6W3sibWV0aG9kSWQiOiJkNGUyYTFiMy03Yzg5LTRmNmQtYTUyMy05ZThiMWM0ZDdmMGEiLCJzZWxlY3RhYmxlIjpmYWxzZSwic2VsZWN0ZWQiOmZhbHNlLCJwcm9wZXJ0aWVzIjp7ImRlc2NyaXB0aW9uIjp7ImRlc2NyaXB0aW9uVGV4dCI6WyJGbGV4aWJsZSBtb250aGx5IHBheW1lbnRzIiwiQ2hvb3NlIGhvdyBtdWNoIHRvIHBheSBlYWNoIG1vbnRoIl0sImRlc2NyaXB0aW9uRm9ybWF0IjoidmVydGljYWwtbGlzdCJ9fSwib3ZlcnJpZGVzIjp7ImxpbmtzIjpbeyJsYWJlbCI6IkZ1bGwgdGVybXMgYW5kIGNvbmRpdGlvbnMiLCJ0eXBlIjoiU0VLS0kiLCJ1cmwiOiIjIn1dfX1dLCJwcm9wZXJ0aWVzIjp7InRpdGxlIjoiUmV2b2x2aW5nIENyZWRpdCIsInN1YnRpdGxlIjoiRmxleGlibGUgcGF5bWVudHMgd2l0aCByZXZvbHZpbmcgY3JlZGl0In19"
}];

export const setupMockWidgets = () => {
  if (!document.getElementById('resurs-payment-widget-script')) {
    const script = document.createElement('script');
    script.id = 'resurs-payment-widget-script';
    script.src = 'https://static.checkout.int.resurs.cloud/payment/resurs-payment-elements-mock.js';
    script.type = 'module';
    script.crossOrigin = 'anonymous';
    document.head.appendChild(script);
  }
  if (!window.__resursPatchedFetch) {
    window.__resursPatchedFetch = window.fetch;
    window.fetch = function (url, options) {
      if (typeof url === 'string' && url.includes('payment-method-groups')) {
        return Promise.resolve({
          ok: true,
          json: () => Promise.resolve(paymentSelectorMockData)
        });
      }
      return window.__resursPatchedFetch.call(this, url, options);
    };
  }
};

{setupMockWidgets()}

Build your own accordion, tabs, or any layout. This component is a pure content renderer; no fetching, no global listeners, and no expand/collapse behavior. You render the title, subtitle, logos, and surrounding UI. The component handles the content area, including regulatory compliance and formatting.

<Note>
  Because you render the subtitle yourself, the component can't read its size; it's a sibling in your DOM, and CSS can't inherit sideways. To make the expanded content match your subtitle, set `font-size` on the group to the same value: `resurs-payment-method-group { font-size: 14px; }`. See [Font size](/developer-guide/checkout/payment-methods/theming#font-size).
</Note>

## Fetch payment method groups

<Tabs>
  <Tab title="Frontend (JS API)">
    ```js theme={"theme":{"light":"catppuccin-latte","dark":"one-dark-pro"}}
    const groups = await resurs.getPaymentMethodGroups({
      sessionId: '5a94e1cf-c2d6-4f70-bc88-2db1d0ad1bc3',
      amount: '5000',
      customerType: 'NATURAL',
      flattened: true,
    });
    ```
  </Tab>

  <Tab title="Backend (REST API)">
    ```http theme={"theme":{"light":"catppuccin-latte","dark":"one-dark-pro"}}
    GET {baseUrl}/sessions/{sessionId}/payment-method-groups?amount=5000&customerType=NATURAL&flattened=true HTTP/1.1
    ```

    You don't need to pass the JWT token, the request is authorized through the sessionId.
  </Tab>
</Tabs>

The `flattened` parameter is optional. When `true`, every payment method is returned as its own group with a single entry in `methodIds`. When `false` or omitted, related payment methods may be grouped together (e.g. part-payment options bundled into one group).

**Response shape:**

```ts theme={"theme":{"light":"catppuccin-latte","dark":"one-dark-pro"}}
[{
  id: string,             // Unique identifier for the group
  methodIds: string[],    // Identifiers for all payment methods in the group
  title: string,          // Display title (e.g. "Invoice")
  subtitle: string,       // Display subtitle
  type: string,           // e.g. "RESURS_INVOICE"
  customerTypes: string[],// e.g. ["NATURAL"]
  internalData: string    // Opaque data, pass directly to the component
}]
```

<Warning>
  `internalData` is opaque. Don't parse, modify, or inspect it. Pass it directly to the component as-is.
</Warning>

## Interactive example

<PaymentMethod methods={paymentSelectorMockData} />

## Render the components

```js theme={"theme":{"light":"catppuccin-latte","dark":"one-dark-pro"}}
let selectedId = null;
const container = document.querySelector('#payment-methods');

groups.forEach(group => {
  const header = document.createElement('button');
  header.textContent = group.title;
  container.appendChild(header);

  const el = document.createElement('resurs-payment-method-group');
  el.data = group.internalData;
  container.appendChild(el);

  header.addEventListener('click', () => {
    // Deactivate all: each one collapses beneath its own header,
    // it is not hidden
    container.querySelectorAll('resurs-payment-method-group').forEach(w => {
      w.active = false;
      w.selectedId = null;
    });
    // Activate clicked
    el.active = true;
  });
});

// Handle the controlled round-trip: store the id and pass it back
container.addEventListener('resursPaymentMethodSelected', (e) => {
  selectedId = e.detail.id;
  const active = container.querySelector('resurs-payment-method-group[active]');
  if (active) active.selectedId = selectedId;
});

container.addEventListener('resursPaymentMethodDeselected', () => {
  selectedId = null;
});
```

Every group stays in the DOM regardless of `active`: toggling it only switches between the expanded and collapsed views under the header you built.

## Props

| Prop          | Type      | Required | Description                                                                                                                                                                                                            |
| ------------- | --------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data`        | `string`  | Yes      | The `internalData` value from the payment method group response.                                                                                                                                                       |
| `active`      | `boolean` | No       | Whether this group is expanded. When `false`, the group renders in a collapsed state, displaying payment method copy. This may include legally required content, such as representative examples. Defaults to `false`. |
| `selected-id` | `string`  | No       | The `id` from the most recent `resursPaymentMethodSelected` event. Pass this back to complete the controlled round-trip.                                                                                               |

## Controlled component pattern

This component is **fully controlled** and never mutates its own props. When the user interacts with the component (for example, by selecting an installment option), it fires a `resursPaymentMethodSelected` event. To update the UI, pass the `id` from that event back to the component via the `selected-id` prop.

When `active` is `false`, the group renders a collapsed view instead of being hidden. This view displays payment method copy, which may include legally required content such as representative examples. If a payment method has no collapsed copy, nothing is rendered and no space is reserved.

Changing `active` to `true` expands the group and fires the initial `resursPaymentMethodSelected` event described below.

<Warning>
  Never hide, unmount, or `display: none` an inactive group. Its collapsed view shows legally required copy that must stay visible. Build accordions or tabs by toggling `active` only; every group must remain rendered.
</Warning>

```mermaid theme={"theme":{"light":"catppuccin-latte","dark":"one-dark-pro"}}
sequenceDiagram
    participant Y as Your Code
    participant W as resurs-payment-method-group

    Y->>W: Set data, active
    Note over W: Renders content
    W-->>Y: resursPaymentMethodSelected { id }
    Note over Y: Store id
    Y->>W: Set selected-id = id
    Note over W: UI re-renders with selection
```

When `active` becomes `true`, the component fires an initial `resursPaymentMethodSelected` event with the default selection. Always handle this event. The `id` is what you pass to the Merchant API when creating a payment.

## Payment method icon

`<resurs-payment-method-icon>` renders the brand icon for a given payment method. Pass a payment method type via the `type` attribute and the component displays the corresponding logo.

<ParamField path="type" type="string">
  Payment method type, from `type` in the payment methods group response. If an unsupported `type` is passed, a warning is logged to the console and no icon is rendered.
</ParamField>

### Resurs payment methods

| Name                        | Icon                                                          | Type                               |
| --------------------------- | ------------------------------------------------------------- | ---------------------------------- |
| Invoice                     | <PaymentMethodIcon type="RESURS_INVOICE" />                   | `RESURS_INVOICE`                   |
| Part payment (select later) | <PaymentMethodIcon type="RESURS_PART_PAYMENT_SELECT_LATER" /> | `RESURS_PART_PAYMENT_SELECT_LATER` |
| Part payment (select now)   | <PaymentMethodIcon type="RESURS_PART_PAYMENT_SELECT_NOW" />   | `RESURS_PART_PAYMENT_SELECT_NOW`   |
| Resurs Card                 | <PaymentMethodIcon type="RESURS_CARD" />                      | `RESURS_CARD`                      |
| Revolving Credit            | <PaymentMethodIcon type="RESURS_REVOLVING_CREDIT" />          | `RESURS_REVOLVING_CREDIT`          |
| New Revolving Credit        | <PaymentMethodIcon type="RESURS_NEW_REVOLVING_CREDIT" />      | `RESURS_NEW_REVOLVING_CREDIT`      |
| Generic                     | <PaymentMethodIcon type="GENERIC" />                          | `GENERIC`                          |
| Zero                        | <PaymentMethodIcon type="ZERO" />                             | `ZERO`                             |

### Third-party payment methods

| Name                     | Icon                                   | Type        |
| ------------------------ | -------------------------------------- | ----------- |
| Swish                    | <PaymentMethodIcon type="SWISH" />     | `SWISH`     |
| Trustly                  | <PaymentMethodIcon type="TRUSTLY" />   | `TRUSTLY`   |
| Vipps                    | <PaymentMethodIcon type="VIPPS" />     | `VIPPS`     |
| MobilePay                | <PaymentMethodIcon type="MOBILEPAY" /> | `MOBILEPAY` |
| Visa / Mastercard (CARD) | <PaymentMethodIcon type="CARD" />      | `CARD`      |
| Visa / Mastercard (D2I)  | <PaymentMethodIcon type="D2I" />       | `D2I`       |
| Visa / Mastercard (NETS) | <PaymentMethodIcon type="NETS" />      | `NETS`      |

### Example

```html theme={"theme":{"light":"catppuccin-latte","dark":"one-dark-pro"}}
<resurs-payment-method-icon type="SWISH"></resurs-payment-method-icon>
```

### Styling

The `<resurs-payment-method-icon>` component renders SVG logos and can be styled with standard CSS. The SVG scales proportionally, so setting one dimension (e.g. `height`) and leaving the other as `auto` keeps the aspect ratio.

```html Custom sizing via inline style theme={"theme":{"light":"catppuccin-latte","dark":"one-dark-pro"}}
<resurs-payment-method-icon
  type="INVOICE"
  style="height: 10px; width: auto;"
></resurs-payment-method-icon>
```

```css Custom sizing via CSS theme={"theme":{"light":"catppuccin-latte","dark":"one-dark-pro"}}
resurs-payment-method-icon {
  height: 10px;
  width: auto;
}
```

You can also wrap the icon in a container and style the surrounding layout:

```css Badge-style icon theme={"theme":{"light":"catppuccin-latte","dark":"one-dark-pro"}}
.icon-badge {
  display: inline-flex;
  align-items: center;
  padding: 6px 10px;
  border: 1px solid #e5e7eb;
  border-radius: 4px;
  background-color: #f0fdf4;
}
```

```html theme={"theme":{"light":"catppuccin-latte","dark":"one-dark-pro"}}
<div class="icon-badge">
  <resurs-payment-method-icon type="INVOICE"></resurs-payment-method-icon>
</div>
```
