Hvordan konfigurere betaling i din Altinn-app
Følg disse grunnleggende stegene for å komme i gang med å integrere betaling i din Altinn App.
1. Før du starter
Organisasjonen du lager appen for må ha en Nets Easy avtale. Du finner informasjon om hvordan du oppretter avtalen her: payments.nets.eu.
2. Legg til en betalingsoppgave i appens prosess, med tilhørende konfigurasjon
Oppsettet i denne seksjonen gjøres fra Prosess-siden i appens arbeidsflate. Naviger dit ved å trykke på “Prosess” i toppmenyen fra appens arbeidsflate.
- Legg til en betalingsoppgave i prosessen ved å velge betalingsoppgave fra menyen til venstre i prosess-editoren
og dra den inn i prosessen. Følgende konfigurasjoner oppdateres automatisk:
- Det legges til 2 datatyper knyttet til betaling (en for data om betaling, en for betalingskvittering).
- Det legges til en ny sidegruppe tilknyttet betaling, med ferdig en ferdig oppsatt side.
- Det legges til en ny regel for tilgangsstyring knyttet til betalingsoppgaven - denne må konfigureres senere.
- Legg til en Gateway etter betalingssteget
- Gatewayen bør ha 2 sekvensflyter ut:
- En som peker videre i prosessen.
- En som peker tilbake til forrige oppgave i prosessen (før betaling).
- Legg til regler som bestemmer når sekvensflytene som skal brukes:
- Klikk på sekvensflyten som går videre i prosessen.
- I panelet til høyre, klikk på “Legg til en ny logikkregel”. Denne settes da opp med aksjonen “Avvise” (
reject
) som utgangspunkt. - Klikk på “Endre” og endre fra “Avvise” til “Bekreft”. Klikk på “Lagre og lukk” for å lagre regelen.
- I panelet til høyre, klikk på “Legg til en ny logikkregel”. Denne settes da opp med aksjonen “Avvise” (
- Klikk på sekvensflyten som går tilbake i prosessen. Gjenta stegene over, men denne gangen trenger du ikke å endre det oppsettet som legges til - aksjon skal være “Avvise”.
- Klikk på sekvensflyten som går videre i prosessen.
- Gatewayen bør ha 2 sekvensflyter ut:
Her følger en beskrivelse for hvordan man setter opp en betalingsoppgave i prosessen. Dette involverer flere steg, i flere konfigurasjonsfiler. Om du bruker Altinn Studio Designer, så blir all denne konfigurasjonen satt opp automatisk når du legger til betalingsoppgaven i prosessen.
Opprett to datatyper for å lagre betalingsinformasjon:
Den første datatypen benyttes av betalingssteget for å lagre informasjon og status om betalingen. Legg den i App/config/applicationmetadata.json
sin dataTypes
-array.
{
"id": "paymentInformation",
"allowedContentTypes": [
"application/json"
],
"maxCount": 1,
"minCount": 0,
}
Den andre datatypen benyttes for å lagre PDF-kvittering for betalingen. Legg den inn samme sted.
{
"id": "paymentReceiptPdf",
"allowedContentTypes": [
"application/pdf"
],
"maxCount": 1,
"minCount": 0,
}
ID-ene kan settes til noe annet, men det må matche ID-ene som legges inn i paymentDataType
og paymentReceiptPdfDataType
i prossessteget, som vist i punktet under.
Utvid app prossesen med payment task:
Det må legges til et prosessteg og en gateway i App/config/process/process.bpmn
, som i eksemplet nedenfor.
Betaling benytter tre user actions. Dersom Altinn-brukergrensesnittet brukes av appen, så vil disse bli kalt automatisk når man står i betalingssteget. Om kun API-et benyttes, så må disse kalles manuelt via /actions
-endepunktet.
pay
: Setter i gang betalingen, ofte ved å gjøre API-kall til betalingsbehandler. Informasjon og status om den igangsatte betalingen lagres i en JSON-datatype som angis i prosesssteget for betaling.confirm
: Kalles når betaling er ferdig gjennomført for å drive prosessen videre til neste steg.reject
: Dersom sluttbruker ser noe feil med ordren, så kan vedkommende trykke “Tilbake” i betalingssteget. Da kanselleres betalingen og informasjon om den avbrutte betalingen slettes. Hvilket prosessteg man deretter ledes til angis i en gateway, som eksemplifisert nedenfor.
<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å samsvare med ID-en til datatypen du konfigurerte i forrige steg. Det samme gjelder datatypen for pdf-kvittering.
Legg til sidegruppe for betaling
Legg til en ny mappe under
App/ui
for betalingsoppgaven din. Kall den f.eks. “payment”.Oppdater filen
App/ui/layout-sets.json
med ny sidegruppe, som har sammeid
som mappen du nettopp opprettet. Din oppdatertelayout-sets.json
kan se slik ut:{ "$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layout-sets.schema.v1.json", "sets": [ { "id": "form", "dataType": "model", "tasks": [ "Task_1" ] }, { "id": "payment", "dataType": "model", "tasks": [ "Task_2" ] } ] }
I din payment layoutSet mappe, legg til en ny fil,
payment.json
, med følgende layout:{ "$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 vil betalingssteget ditt bare vise en hvit side.
3. Gi tilganger til den som skal betale
Dette steget foregår i appens prosessverktøy. Naviger til appens arbeidsflate, og velg “Prosess” fra toppmenyen.
- Velg betalingsoppgaven i prosessverktøyet ved å klikke på den.
- Se at konfigurasjonspanelet på høyre side oppdateres med detajer fra oppgaven.
- Klikk på “Tilganger”-seksjonen for å ekspandere den, og velg “Gå til Tilganger”.
- Scroll ned til bunnen av modalen, og se at det er en regel som er markert i rødt.
- Legg til rollen(e) som skal ha tilgang til betalingsoppgaven i regelen som er markert i rødt.
- Når rolle er lagt til, er regelen gyldig og markeres ikke lenger i rødt.
- Lukk Innstillinger-modalen ved å klikke på X-en øverst til høyre.
read
, write
, pay
, confirm
og reject
på betalingsprosessteget.4. Konfigurer visning av betalingsinformasjon
Oppsettet i denne seksjonen gjøres fra Lage-siden i appens arbeidsflate. Naviger dit ved å trykke på “Lage” i toppmenyen fra appens arbeidsflate.
Hvordan vise betalingsinformasjon i skjemaet
Dette steget skal gjøres i sidegruppen som er tilknyttet selve skjemaet. Du kan bytte mellom sidegrupper i nedtrekkslisten øverst til venstre på Lage-siden. Skjemaoppgaven som følger med appen når den opprettes er tilknyttet en sidegruppe som heter “form”. Dersom du har lagt til andre skjemaoppgaver i prosessen, har sidegruppen samme ID som oppgaven i prosessen.
Dra komponenten “Betalingsdetaljer” inn i skjemaet. Denne komponenten viser en tabell som viser elementene brukeren må betale for.
- Komponenten ligger nederst i “Avansert” i komponentkolonnen til venstre på siden.
Du kan plassere denne komponenten hvor som helst i skjemaet ditt, men vi anbefaler å i det minste sette det på den siste siden før brukeren blir bedt om å betale.
For å få oppdatert ordrelinjene etter hvert som data som brukes til å beregne ordrelinjer endres, må du legge til en mapping til datafeltene som brukes til å beregne ordrelinjene. Dette gjøres foreløpig manuelt, direkte i layout-filene:
{
"id": "paymentDetails",
"type": "PaymentDetails",
"textResourceBindings": {
"title": "Oversikt over betaling",
"description": "Her er en oversikt over hva du skal betale for."
},
"mapping": {
"GoodsAndServicesProperties.Inventory.InventoryProperties": "paymentDetails"
}
}
Hvordan vise betalingsinformasjon i betalingsssteget
Dette er satt opp automatisk dersom du brukte Altinn Studio Designer og prosessverktøyet til å sette opp betalingssteget.
Hvordan sette opp egen visning for kvittering for betaling (valgfritt)
Dette steget er valgfritt. Dersom man ikke gjennomfører dette steget, vil oppsettet fra betalingssteget benyttes.
- Påse at det er utforming for betalingssteget som vises på Utforming-siden.
- Klikk på “Legg til ny side”.
- I konfigurasjonspanelet for siden, åpne “PDF” og klikk på “Gjør om siden til PDF”.
- Legg til komponenten “Betaling”, og ev. andre tekster og komponenter du ønsker på PDF-siden.
- Forhåndsvis PDF-visning ved å klikke på “utviklerverktøy” knappen nederst til høyre i forhåndsvisningen
- Klikk på knappen “Forhåndsvis PDF”
Legg til OrderDetails-komponenten i skjemaet ditt
Dette vil vise en tabell som viser elementene brukeren må betale for. Du kan plassere dette hvor som helst i appen din, men vi anbefaler å i det minste sette det på den siste siden før brukeren blir bedt om å betale.
For å få oppdatert ordrelinjene etter hvert som data som brukes til å beregne ordrelinjer endres, må du legge til en mapping til datafeltene som brukes til å beregne ordrelinjene.
{
"id": "paymentDetails",
"type": "PaymentDetails",
"textResourceBindings": {
"title": "Oversikt over betaling",
"description": "Her er en oversikt over hva du skal betale for."
},
"mapping": {
"GoodsAndServicesProperties.Inventory.InventoryProperties": "paymentDetails"
}
}
Legg til layout for kvitteringen (Valgfritt)
Hvis du vil vise mer informasjon på kvittering som vises til kunden, må du legge til en egen tilpasset layout for den.
Legg til en layout fil, f.eks. receiptLayout.json
.
Her er et minimalt eksempel:
{
"$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 din layoutSet-settings.json-fil, og spesifiser din kvitteringslayout i pdfLayoutName
feltet:
{
"$schema": "https://altinncdn.no/toolkits/altinn-app-frontend/4/schemas/json/layout/layoutSettings.schema.v1.json",
"pages": {
"order": [
"payment"
],
"pdfLayoutName": "paymentReceipt",
"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 ytterligere informasjon.
5. Beregn hva som skal betales
Dette steget må gjøres i kode. Se “Manuelt oppsett”-fanen for denne seksjonen for veiledning.
Implementer IOrderDetailsCalculator interfacet i C#
Legg til en ny klasse der du har din custom kode, f.eks: App/logic/OrderDetailsCalculator.cs
.
Her vil du implementere din logikk for å regne ut hva brukeren skal betale for. Du kan for eksempel aksessere skjemadata, legge til obligatoriske avgifter, eller kun legge til en fast kostnad for skjemaet.
Returverdien fra metoden CalculateOrderDetails
angir:
- Betalingsbehandler som skal benyttes for ordren. Disse tilgjengeliggjøres ved å implementere interfacet
IPaymentProcessor
og registrere dem somtransient
iprogram.cs
. Fyll utNets Easy
for å benytte standardimplementasjon for Nets Easy. - Valuta
- Ordrelinjer
- Detaljer om betalingsmottaker. Brukes i kvittering.
- Detaljer om betaler (valgfritt), dersom du ønsker å forhåndsutfylle denne informasjonen hos Nets Easy. Kan brukes i kombinasjon med Nets Easy sitt flagg
MerchantHandlesConsumerData
, som vi har eksponert via appsettings.jsonNetsPaymentSettings.MerchantHandlesConsumerData
. Om den er satt tiltrue
så må detaljer om betaler sendes med, ellers feiler det.
I dette eksempelet regnes ordrelinjene ut 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
:
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
Dette steget må gjøres manuelt. Støtte for konfigurasjon i Altinn Studio kommer i løpet av høsten 2024. Se “Manuelt oppsett”-fanen for denne seksjonen for veiledning.
Legg til config i appSettings.json:
- Hent din hemmelige nøkkel fra Nets.. Pass på at du bruker testnøkkelen under utvikling.
- Gjør appen din klar for bruk av Azure Key Vault som konfigurasjonkilde, om dette ikke allerede er gjort tidligere. Se relevant dokumentasjon.
- Legg til din hemmelige nøkkel i Key Vault, med variabelnavnet
NetsPaymentSettings--SecretApiKey
. På denne måten vil den overstyreSecretApiKey
iappsettings.json
. - Legg til
NetsPaymentSettings
i dinappsettings.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
iappsettings.Production.json
. Eksempelet ovenfor inneholder URL til test API-et. - Feltet
MerchantHandlesConsumerData
er frivillig, og styrer hvorvidt du skal sende med detaljer om betaler, eller om det skal fylles ut i Nets Easy sin betalingsside. - PaymentMethodsConfiguration fungerer likt som beskrevet i Nets sin dokumentasjon her.
- Husk å sette riktig
- Lokal mocking av
SecretApiKey
kan gjøres ved hjelp av user secrets.dotnet user-secrets init dotnet user-secrets set "NetsPaymentSettings:SecretApiKey" "test-secret-key-used-for-documentation"