Sist endret: 30. okt. 2026
Preview: Dette er dokumentasjonen for Altinn Studio v9, som er en forhåndsversjon. Gjeldende versjon er v8. For å se dokumentasjonen for gjeldende versjon, velg v8 i versjonsvelgeren i menyen til venstre.

Slik setter du opp betaling i appen

Slik setter du opp betaling med Nets Easy i appen din.

1. Før du starter

Organisasjonen du lager appen for, må ha en Nets Easy-avtale. Les hvordan du oppretter avtalen på nettsidene til Nets.

2. Legge til en betalingsoppgave i appens prosess, med tilhørende konfigurasjon

    Du gjør oppsettet i denne delen på siden Arbeidsflyt i Altinn Studio. Klikk på Arbeidsflyt i toppmenyen for å komme dit.

    1. Legg til en betalingsoppgave

    Legg til en betalingsoppgave ved å dra Lag betalingsoppgave fra menyen til venstre inn i arbeidsflyten.

    Altinn Studio legger da automatisk til

    • to datatyper for betaling (en for betalingsdata og en for betalingskvitteringen)
    • sider for betalingsoppgaven, med én ferdig oppsatt side
    • en regel for tilgangsstyring for betalingsoppgaven, som du setter opp i steget «Gi tilgang til den som skal betale og tjenesteeieren»

    2. Legge til en gateway etter betalingsoppgaven

    Gatewayen skal ha to piler (sekvensflyter) ut:

    • En som peker videre i arbeidsflyten.
    • En som peker tilbake til forrige oppgave i arbeidsflyten (før betaling).

    3. Sett opp regler for pilene ut av gatewayen

    For pilen som går videre:

    1. Klikk på pilen som går videre i arbeidsflyten.
    2. I panelet til høyre klikker du på Lag en ny logikkregel. Regelen får handlingen Avvise (reject) som utgangspunkt.
    3. Klikk på Endre, og bytt handlingen fra Avvise til Bekrefte.
    4. Klikk på Lagre og lukk for å lagre regelen.

    For pilen som går tilbake:

    1. Klikk på pilen som går tilbake i arbeidsflyten.
    2. Gjenta stegene over, men ikke endre handlingen. Den skal være Avvise.

    Arbeidsflyt med oppgavene Utfylling og Betaling, og en gateway som enten går videre til slutten eller tilbake til Utfylling
    Arbeidsflyt med betaling

    Her beskrives hvordan du setter opp en betalingsoppgave i prosessen. Dette involverer flere steg i flere konfigurasjonsfiler. Hvis du bruker Altinn Studio Designer, setter systemet opp all denne konfigurasjonen automatisk når du legger til betalingsoppgaven i prosessen.

    Opprette to datatyper for å lagre betalingsinformasjon

    Betalingssteget bruker den første datatypen for å lagre informasjon og status om betalingen. Legg den i dataTypes-arrayen i App/config/applicationmetadata.json.

    App/config/applicationmetadata.json
    {
        "id": "paymentInformation",
        "allowedContentTypes": [
          "application/json"
        ],
        "allowedContributors": [
          "app:owned"
        ],
        "maxCount": 1,
        "minCount": 0,
    }
    

    Systemet bruker den andre datatypen for å lagre PDF-kvittering for betalingen. Legg den inn på samme sted.

    App/config/applicationmetadata.json
    {
        "id": "paymentReceiptPdf",
        "allowedContentTypes": [
            "application/pdf"
        ],
        "allowedContributors": [
          "app:owned"
        ],
        "maxCount": 1,
        "minCount": 0,
    }
    

    Det er viktig å sette allowedContributors til "app:owned". Det gjør at du ikke kan redigere disse dataene via appens API, men bare via appen selv. Før versjon 8.6 var denne konfigurasjonen feilstavet allowedContributers.

    ID-ene kan settes til noe annet, men de må være de samme som ID-ene du legger inn i paymentDataType og paymentReceiptPdfDataType i prosessteget, som vist i punktet under.

    Utvide app-prosessen med payment task

    Du må legge til et prosessteg og en gateway i App/config/process/process.bpmn, som i eksemplet nedenfor.

    Betaling bruker tre user actions. Hvis appen bruker Altinn-brukergrensesnittet, kaller systemet disse automatisk når du står i betalingssteget. Hvis du bare bruker API-et, må du kalle disse manuelt via /actions-endepunktet.

    • pay: Setter i gang betalingen, ofte ved å gjøre API-kall til betalingsbehandler. Systemet lagrer informasjon og status om den igangsatte betalingen i en JSON-datatype som du angir i prosesssteget for betaling.
    • confirm: Du kaller denne når betalingen er ferdig gjennomført, for å drive prosessen videre til neste steg.
    • reject: Hvis sluttbrukeren ser noe feil med ordren, kan vedkommende trykke Tilbake i betalingssteget. Da kansellerer systemet betalingen og sletter informasjon om den avbrutte betalingen. En gateway angir hvilket prosessteg du deretter går til, som eksemplifisert nedenfor.
    App/config/process/process.bpmn
        <bpmn:startEvent id="StartEvent_1">
          <bpmn:outgoing>Flow_start_t1</bpmn:outgoing>
        </bpmn:startEvent>
    
        <bpmn:sequenceFlow id="Flow_start_t1" sourceRef="StartEvent_1" targetRef="Task_1" />
    
        <bpmn:task id="Task_1" name="Utfylling">
          <bpmn:incoming>Flow_start_t1</bpmn:incoming>
          <bpmn:incoming>Flow_g1_t1</bpmn:incoming>
          <bpmn:outgoing>Flow_t1_t2</bpmn:outgoing>
          <bpmn:extensionElements>
            <altinn:taskExtension>
              <altinn:taskType>data</altinn:taskType>
            </altinn:taskExtension>
          </bpmn:extensionElements>
        </bpmn:task>
    
        <bpmn:sequenceFlow id="Flow_t1_t2" sourceRef="Task_1" targetRef="Task_2" />
    
        <bpmn:task id="Task_2" name="Betaling">
          <bpmn:incoming>Flow_t1_t2</bpmn:incoming>
          <bpmn:outgoing>Flow_t2_g1</bpmn:outgoing>
          <bpmn:extensionElements>
            <altinn:taskExtension>
              <altinn:taskType>payment</altinn:taskType>
              <altinn:actions>
                <altinn:action>confirm</altinn:action>
                <altinn:action>pay</altinn:action>
                <altinn:action>reject</altinn:action>
              </altinn:actions>
              <altinn:paymentConfig>
                <altinn:paymentDataType>paymentInformation</altinn:paymentDataType>
                <altinn:paymentReceiptPdfDataType>paymentReceiptPdf</altinn:paymentReceiptPdfDataType>
              </altinn:paymentConfig>
            </altinn:taskExtension>
          </bpmn:extensionElements>
        </bpmn:task>
    
        <bpmn:sequenceFlow id="Flow_t2_g1" sourceRef="Task_2" targetRef="Gateway_1" />
    
        <bpmn:exclusiveGateway id="Gateway_1">
          <bpmn:incoming>Flow_t2_g1</bpmn:incoming>
          <bpmn:outgoing>Flow_g1_t1</bpmn:outgoing>
          <bpmn:outgoing>Flow_g1_end</bpmn:outgoing>
        </bpmn:exclusiveGateway>
    
        <bpmn:sequenceFlow id="Flow_g1_t1" sourceRef="Gateway_1" targetRef="Task_1">
          <bpmn:conditionExpression>["equals", ["gatewayAction"], "reject"]</bpmn:conditionExpression>
        </bpmn:sequenceFlow>
        <bpmn:sequenceFlow id="Flow_g1_end" sourceRef="Gateway_1" targetRef="EndEvent_1">
          <bpmn:conditionExpression>["equals", ["gatewayAction"], "confirm"]</bpmn:conditionExpression>
        </bpmn:sequenceFlow>
    
        <bpmn:endEvent id="EndEvent_1">
          <bpmn:incoming>Flow_g1_end</bpmn:incoming>
        </bpmn:endEvent>
    

    NB: Verdien til noden <altinn:paymentDataType>paymentInformation</altinn:paymentDataType> må være den samme som ID-en til datatypen du konfigurerte i forrige steg. Det samme gjelder datatypen for PDF-kvittering.

    Legge til sidegruppe for betaling

    • Legg til en mappe under App/ui med samme navn som ID-en til betalingsoppgaven. I eksempelet over er ID-en Task_2, så mappen må hete Task_2.

    • Legg til Settings.json i mappen. Sett defaultDataType til datamodellen som betalingssteget skal bruke:

      App/ui/Task_2/Settings.json
      {
        "$schema": "https://altinncdn.no/schemas/json/layout/layoutSettings.schema.v1.json",
        "defaultDataType": "model",
        "pages": {
          "order": ["payment"]
        }
      }
      
    • Opprett mappen App/ui/Task_2/layouts, og legg til layout-filen payment.json med dette innholdet:

      App/ui/Task_2/layouts/payment.json
      {
        "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json",
        "data": {
          "layout": [
            {
              "id": "payment-test-component",
              "type": "Payment",
              "textResourceBindings": {
                "title": "Oppsummering"
              }
            }
          ]
        }
      }
      

      Dette er nødvendig for at betaling skal fungere. Uten dette viser betalingssteget ditt bare en hvit side.

    3. Gi tilgang til den som skal betale og tjenesteeieren

      Du gjør dette steget på siden Arbeidsflyt i Altinn Studio. Klikk på Arbeidsflyt i toppmenyen.

      1. Klikk på betalingsoppgaven i arbeidsflyten.
      2. Åpne Tilganger i panelet til høyre, og klikk på Gå til Tilganger. Siden Innstillinger åpner seg med Tilganger valgt.
      3. Velg fanen Regler. Regelen for betalingsoppgaven er markert med rødt og har feilmeldingen «Du må velge minst én rolle eller tilgangspakke.»
      4. Under Hvem skal ha disse rettighetene? legger du til tilgangspakkene eller rollene som skal ha tilgang til betalingsoppgaven, under For virksomheter eller For privatpersoner. Når du har lagt til minst én, er regelen gyldig og ikke lenger markert med rødt.
      5. Klikk på Arbeidsflyt i toppmenyen for å gå tilbake.
      Brukeren som skal betale, må ha rettigheter til handlingene read, write, pay, confirm og reject på betalingsprosessteget.

      Når Nets varsler appen om at betalingen er fullført, oppdaterer appen betalingsinformasjonen og fører prosessen videre på vegne av tjenesteeieren. I App/config/authorization/policy.xml må [org] derfor ha handlingene read og write for appen og confirm for betalingsoppgaven.

      Altinn Studio legger automatisk til standardscopene for tjenesteeier i Maskinporten for apper som bruker Altinn App v9. Kontroller likevel at autorisasjonspolicyen gir tjenesteeieren de nødvendige handlingene. Se integrasjon med Maskinporten. Bygg og publiser appen på nytt etter at du har endret scopene eller autorisasjonspolicyen.

      4. Konfigurere visning for betalingsinformasjon

        Du gjør det meste av oppsettet i denne delen på siden Utforming i Altinn Studio. Klikk på Utforming i toppmenyen. Noe av oppsettet gjør du manuelt i layoutfilene, som beskrevet nedenfor.

        Slik viser du betalingsinformasjon i skjemaet

        Du gjør dette steget i skjemaoppgaven. Layoutfilene til oppgaven ligger i App/ui/{taskId}/layouts/. Skjemaoppgaven som følger med appen når du oppretter den, har ID-en Task_1. Hvis du har lagt til andre skjemaoppgaver i arbeidsflyten, bruker du mappen med samme ID som oppgaven.

        • Legg til en komponent av typen PaymentDetails i layoutfilen til siden der brukeren skal se betalingen. Komponenten viser en tabell over det brukeren skal betale for. I Altinn Studio heter den Betalingsdetaljer, men du finner den ikke i Velg komponent, så du legger den til manuelt, som i eksempelet nedenfor.

          Du kan plassere komponenten hvor som helst i skjemaet, men vi anbefaler å sette den på den siste siden før brukeren skal betale.

        • Appen beregner ordrelinjene ut fra data i skjemaet. Når brukeren endrer disse dataene, må appen hente ordrelinjene på nytt. Derfor legger du til datafeltene som appen bruker i beregningen, i refetchDependencies. Hver verdi er et uttrykk som peker på et felt i datamodellen. Du kan velge navnet på nøkkelen fritt, og appen sender ikke verdiene til serveren. Du legger dem inn i layoutfilen, som i eksempelet nedenfor:

        App/ui/{taskId}/layouts/{page}.json
        {
          "id": "paymentDetails",
          "type": "PaymentDetails",
          "textResourceBindings": {
            "title": "Oversikt over betaling",
            "description": "Her er en oversikt over hva du skal betale for."
          },
          "refetchDependencies": {
            "inventory": ["dataModel", "GoodsAndServicesProperties.Inventory.InventoryProperties"]
          }
        }
        

        Slik viser du betalingsinformasjon i betalingsoppgaven

        Altinn Studio setter dette opp automatisk når du legger til betalingsoppgaven i Arbeidsflyt.

        Slik lager du en egen kvittering for betalingen (valgfritt)

        Hvis du hopper over dette steget, bruker appen oppsettet fra betalingsoppgaven i kvitteringen.

        1. Klikk på Utforming i toppmenyen, og klikk på Utform på kortet Oppgave: Betaling. Hvis du allerede står i en oppgave, klikker du først på Oversikt øverst på siden for å komme tilbake til kortene.

        2. Klikk på Legg til ny side.

        3. I panelet til høyre åpner du PDF og klikker på Gjør om siden til PDF.

        4. Legg til komponenten Betaling, og eventuelt andre tekster og komponenter du vil ha på PDF-siden.

        5. Åpne utviklerverktøyet i forhåndsvisningen med knappen nederst til høyre, eller med hurtigtasten Ctrl+Shift+K (⌘+Shift+K på Mac). Klikk deretter på Generer PDF under Forhåndsvis PDF.

          Knappen for å åpne utviklerverktøyet nederst til høyre i forhåndsvisningen
          Åpne utviklerverktøyet

        Legge til PaymentDetails-komponenten i skjemaet ditt

        Dette viser en tabell som viser elementene brukeren må betale for. Du kan plassere dette hvor som helst i appen din, men vi anbefaler å sette det på den siste siden før brukeren blir bedt om å betale.

        Systemet beregner ordrelinjene ut fra data i skjemaet. Når brukeren endrer disse dataene, må appen hente ordrelinjene på nytt. Derfor legger du til datafeltene som systemet bruker i beregningen, i refetchDependencies. Hver verdi er et uttrykk som peker på et felt i datamodellen. Navnet på nøkkelen kan du velge fritt, og verdiene sendes ikke til serveren.

        App/ui/{taskId}/layouts/{page}.json
        {
          "id": "paymentDetails",
          "type": "PaymentDetails",
          "textResourceBindings": {
            "title": "Oversikt over betaling",
            "description": "Her er en oversikt over hva du skal betale for."
          },
          "refetchDependencies": {
            "inventory": ["dataModel", "GoodsAndServicesProperties.Inventory.InventoryProperties"]
          }
        }
        

        Legge til layout for kvitteringen (valgfritt)

        Hvis du vil vise mer informasjon på kvitteringen som vises til kunden, må du legge til en egen tilpasset layout for den.

        Legg til en layout-fil, for eksempel receiptLayout.json.

        Her er et minimalt eksempel:

        App/ui/{taskId}/layouts/receiptLayout.json
        {
          "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout.schema.v1.json",
          "data": {
            "layout": [
              {
                "id": "test",
                "type": "Payment",
                "renderAsSummary": true
              }
            ]
          }
        }
        

        Oppdater Settings.json i UI-mappen til betalingsoppgaven (App/ui/<taskId>/Settings.json), og angi kvitteringslayouten din i feltet pdfLayoutName:

        App/ui/{taskId}/Settings.json
        {
          "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layoutSettings.schema.v1.json",
          "defaultDataType": "model",
          "pages": {
            "order": [
              "payment"
            ],
            "pdfLayoutName": "receiptLayout",
            "showProgress": true,
            "showLanguageSelector": true
          }
        }
        

        Dette er alt du trenger for å vise en gyldig kvittering, men du kan også tilpasse receiptLayout.json ved å legge til ytterligere komponenter, for eksempel en paragrafkomponent hvis du ønsker å legge til mer informasjon.

        5. Beregne hva som skal betales

          Altinn Studio har ikke innstillinger for dette. Du gjør dette steget i koden til appen. Se fanen Manuelt oppsett for dette steget.

          Implementere IOrderDetailsCalculator-interfacet i C#

          Legg til en ny klasse der du har din custom-kode, for eksempel App/logic/OrderDetailsCalculator.cs.

          Her implementerer du logikken din for å regne ut hva brukeren skal betale for. Du kan for eksempel aksessere skjemadata, legge til obligatoriske avgifter eller bare legge til en fast kostnad for skjemaet.

          Returverdien fra metoden CalculateOrderDetails angir:

          • Betalingsbehandler som ordren skal bruke. Du gjør disse tilgjengelige ved å implementere interfacet IPaymentProcessor og registrere dem som transient i program.cs. Fyll ut Nets Easy for å bruke standardimplementasjonen for Nets Easy.
          • Valuta
          • Ordrelinjer
          • Detaljer om betalingsmottaker. Brukes i kvittering.
          • Detaljer om betaler (valgfritt), hvis du ønsker å forhåndsutfylle denne informasjonen hos Nets Easy. Du kan bruke dette i kombinasjon med Nets Easy sitt flagg MerchantHandlesConsumerData, som vi har eksponert via appsettings.json NetsPaymentSettings.MerchantHandlesConsumerData. Hvis du setter den til true, må du sende med detaljer om betaler, ellers feiler det.

          I dette eksempelet regner systemet ut ordrelinjene basert på skjemadata:

          public class OrderDetailsCalculator : IOrderDetailsCalculator
          {
              private readonly IDataClient _dataClient;
          
              public OrderDetailsCalculator(IDataClient dataClient)
              {
                  _dataClient = dataClient;
              }
              
              public async Task<OrderDetails> CalculateOrderDetails(Instance instance, string? language)
              {
                  DataElement modelData = instance.Data.Single(x => x.DataType == "model");
                  InstanceIdentifier instanceIdentifier = new(instance);
                  
                  Form formData = (Form) await _dataClient.GetFormData(instanceIdentifier.InstanceGuid, typeof(Form), instance.Org, instance.AppId,
                      instanceIdentifier.InstanceOwnerPartyId, new Guid(modelData.Id));
          
                  List<PaymentOrderLine> paymentOrderLines = formData.GoodsAndServicesProperties.Inventory.InventoryProperties
                      .Where(x => !string.IsNullOrEmpty(x.NiceClassification) && !string.IsNullOrEmpty(x.GoodsAndServices))
                      .Select((x, index) =>
                          new PaymentOrderLine
                          {
                              Id = index.ToString(), Name = $"{GetLocalizedName(x.Id, language)}", PriceExVat = GetPriceForInventoryItem(x), Quantity = 1, VatPercent = 0M
                          })
                      .ToList();
          
                  return new OrderDetails { 
                    PaymentProcessorId = "Nets Easy", 
                    Currency = "NOK", 
                    OrderLines = paymentOrderLines, 
                    Receiver = GetReceiverDetails(),
                    Payer = GetPayerDetails()};
              }
          }
          

          Registrer IOrderDetailsCalculator-implementasjonen i program.cs:

          App/Program.cs
          void RegisterCustomAppServices(IServiceCollection services, IConfiguration config, IWebHostEnvironment env)
          {
              // Register your apps custom service implementations here.
              services.AddTransient<IOrderDetailsCalculator, OrderDetailsCalculator>(); 
          }
          

          6. Koble appen til Nets Easy-avtalen

            Altinn Studio har ikke innstillinger for dette. Du kobler appen til Nets Easy-avtalen manuelt: Du legger den hemmelige nøkkelen fra Nets i Azure Key Vault og legger til innstillingene i appsettings.json. Se fanen Manuelt oppsett for dette steget.

            Legge til config i appSettings.json

            1. Hent din hemmelige nøkkel fra Nets. Pass på at du bruker testnøkkelen under utvikling.

            2. Gjør appen din klar for bruk av Azure Key Vault som konfigurasjonskilde, hvis du ikke har gjort det allerede. Se Hemmeligheter.

            3. Legg til din hemmelige nøkkel i Key Vault med variabelnavnet NetsPaymentSettings--SecretApiKey. På denne måten overstyrer den SecretApiKey i appsettings.json.

            4. Legg til NetsPaymentSettings i din appsettings.json.

              App/appsettings.json
              {
                "NetsPaymentSettings": 
                {
                  "SecretApiKey": "In keyvault",
                  "BaseUrl": "https://test.api.dibspayment.eu/",
                  "TermsUrl": "https://www.yourwebsite.com/terms",
                  "ShowOrderSummary": true,
                  "ShowMerchantName": true,
                  "MerchantHandlesConsumerData": true,
                  "PaymentMethodsConfiguration": [
                    { "Name": "Visa", "Enabled": true },
                    { "Name": "MasterCard", "Enabled": true }
                  ]
                }
              }
              
              • Husk å sette riktig baseUrl i appsettings.Production.json. Eksemplet ovenfor inneholder URL til test-API-et.
              • Feltet MerchantHandlesConsumerData er frivillig og styrer om du skal sende med detaljer om betaleren, eller om det skal fylles ut i Nets Easy sin betalingsside.
              • PaymentMethodsConfiguration fungerer som beskrevet i Nets sin dokumentasjon her.
            5. Du kan gjøre lokal mocking for SecretApiKey ved hjelp av user secrets.

              dotnet user-secrets init
              dotnet user-secrets set "NetsPaymentSettings:SecretApiKey" "test-secret-key-used-for-documentation"
              

            7. Automatisk videreføring etter fullført betaling

            Den innebygde Nets Easy-integrasjonen registrerer en webhook når den oppretter en betaling. Når Nets varsler om en fullført betaling, kontrollerer appen betalingsstatusen hos Nets, oppdaterer betalingsinformasjonen og fører betalingsoppgaven videre hvis statusen er Paid. Dette skjer på serveren og er ikke avhengig av at brukeren returnerer til appen etter betalingen.

            Appen fullfører betalingsoppgaven automatisk. Hvis neste steg i prosessen er en slutthendelse, blir også instansen fullført. Ellers går instansen videre til neste prosessteg.

            For at automatisk videreføring skal fungere:

            • Sett opp tilgangene for tjenesteeieren som beskrevet i steg 3.
            • Bygg og publiser appen på nytt etter konfigurasjonsendringer.
            • Start en ny betaling etter publisering. Appen registrerer webhooken når den oppretter betalingen, så betalinger som ble opprettet før endringen, får ikke den nye callbacken.

            Appen registrerer ikke webhooken når du kjører den lokalt. Test derfor denne flyten i et publisert testmiljø.