Byg din egen butiksfront på Shoporamas headless API
Guide til udviklere: byg din egen butiksfront på shoppens /api med varer, sider fra Sidedesigneren, kurv, rabatkoder, checkout, betaling, og kundekonto. Den fulde reference ligger på shoporama.dk/headless.
Hver Shoporama-shop har et headless API, som du kan bygge din egen butiksfront på: varer, sider fra Sidedesigneren, kurv, rabatkoder, checkout, betaling, og kundekonto. API'et bruger shoppens egen motor, så shoppens regler for priser, lager, fragt, og betaling gælder hele vejen. Her gennemgår vi flowet fra første sidevisning til kvitteringen og de faldgruber, du skal kende. Alle endpoints og felter står i referencen til det headless API.
Headless API eller REST API?
Shoporama har to API'er, og de løser hver sin opgave.
- Headless API'et ligger på shoppens egen adresse, https://din-shop.dk/api. Det kræver ingen nøgle og arbejder med kundens egne rettigheder, præcis som når kunden handler i shoppen. Kurv, checkout, og login følger kundens cookies. Det er API'et til en butiksfront.
- REST API'et bruger en API-nøgle fra Integrationer → API-adgang eller OAuth og kan oprette og rette varer, ordrer, kunder, og meget mere. Det er til integrationer og dine egne systemer, og nøglen må aldrig ligge i en browser. Læs mere i REST API og OAuth.
Sender du en REST-nøgle til headless API'et, bliver den ignoreret. Headless betyder, at butikkens udseende er skilt fra den del, der holder styr på varer, kurv, og ordrer. Se headless commerce og REST API i ordbogen.
Før du starter: domæne, cookies, og lande
Kurv, checkout, og login virker kun, når din butiksfront kører på shoppens primære domæne (eller et lands eget domæne) og kalder API'et på samme domæne. Det primære domæne står under Webshop → Domæne.
- Kurven, adressen, og kundens login ligger i cookies (sessionen, basket, og customer_token), ligesom i shoppen. Kald derfor API'et fra browseren med credentials: 'include'. Se Hvilke cookies sætter Shoporama?
- API'et tillader kun kald med cookies fra shoppens egne domæner. Til alle andre domæner svarer API'et med Access-Control-Allow-Credentials: false, så browseren afviser et kald med cookies.
- Katalog og indhold (varer, kategorier, søgning, sider, og menuer) kan hentes fra alle domæner. Lav de kald uden credentials: 'include'.
- Der er ingen token-adgang til kurven. API'et finder kurven ud fra kundens cookies og ikke ud fra en header eller et token i adressen. Feltet token i kurvens svar kan ikke bruges til at hente kurven i et andet kald.
- Domænerne under "Flere domæner" sender alle forespørgsler videre til det primære domæne, også kald til /api. Kør derfor butiksfronten på det primære domæne og ikke fx på www-udgaven, hvis den ikke er den primære.
- Efter betalingen kommer kunden altid tilbage til shoppens egen /thanks på shoppens (eller landets) adresse. Det kan ikke ændres pr. kald.
Den enkleste vej er derfor at bygge butiksfronten som et tema, ligesom Washington. Et tema består af Smarty-skabeloner, se Temaer: opret og redigér dine egne tema-skabeloner. Ud over selve appen skal temaet have skabelonerne til betalingssiden (payment.html), kvitteringen (thanks.html), kundesiderne (user-...html), og fortrydelsen (withdrawal.html). Så kører alle sider på shoppens domæne, også betalingssiden, kvitteringen, kundesiderne, og fortrydelsen. Skal shoppen have sit eget domæne, så se Flyt din shop til eget domæne.
Sælger shoppen i flere lande, bruger du landets adresse, fx https://din-shop.dk/en/api eller /api på landets eget domæne. Så følger sprog, valuta, priser, leveringslande, og betalingsmetoder landet. Brug samme adresse i hele sessionen, ellers skifter sprog og valuta undervejs. Til resolve sender du stien uden landets præfiks, fx /en/api/resolve/coffee og ikke /en/api/resolve/en/coffee. Adresserne i menuerne har præfikset med. Se Sælg i flere lande og på flere sprog og Gør dit tema klar til flere sprog og lande.
Svarformat og fejlkoder
Alle svar har samme form. Et kald, der lykkes, har sine data i data. En fejl har HTTP-statuskoden, en besked, og en årsag i error.details.reason:
{ "success": true, "data": { ... } }
{
"success": false,
"error": {
"code": 400,
"message": "Payment method not selected",
"details": { "reason": "payment_missing" }
}
}
Alle fejl har en reason. Brug den til dine egne tekster til kunden, og ikke message, som kan være på dansk eller engelsk. Dem du typisk skal håndtere:
- Kurv: requires_variant, sold_out, stock (med available, det antal der er på lager), product_not_found, product_unavailable, not_added, customer_input (med attribute_errors for felter, kunden selv udfylder), amount_missing, og amount_invalid.
- Rabatkoder: voucher_missing, voucher_not_found, og voucher_expired. Kurven ligger i details.basket.
- Point: points_invalid (med available, de point kurven kan bære) og loyalty_inactive. Uden login giver point login_required.
- Levering og betaling: shipping_invalid, payment_missing, og payment_not_available.
- Gennemfør ordren: empty_basket, stock, basket_unavailable, min_order_value (med beløbet ekskl. moms), delivery_country, address_missing, blocked, order_in_progress, order_failed, og payment_link_failed.
- Kundekonto og ordrer: login_missing, login_failed, login_required, order_not_found, og order_verification.
- Generelle: fejl uden en mere præcis årsag får en ud fra statuskoden, fx not_found, bad_request, method_not_allowed, request_too_large, rate_limited, og internal_error. En serverfejl viser aldrig en intern fejltekst.
API'et har en grænse på 300 kald i minuttet pr. session. Derefter svarer det med HTTP 429 og rate_limited, indtil næste minut begynder. Shoppen har desuden en grænse pr. IP-adresse for alle forespørgsler, også dem uden cookies. Overskrides den, afvises IP-adressen i en time med HTTP 429 som almindelig tekst og ikke JSON.
API'et har ingen version i adressen. Nye felter kan komme til, så skriv din kode, så den ignorerer felter, den ikke kender.
Hent varer, kategorier, og søgning
- GET /api/products giver de varer, der er online, og hvis brand kunden må se. Brug limit (højst 250, ellers shoppens sidestørrelse), offset, og category_id. Svaret har pagination med total og antal sider. ?gtin= slår en stregkode op.
- GET /api/products/{id} giver én vare. Du kan også bruge varens rewrite_url i stedet for id'et.
- GET /api/frontpage giver varerne i shoppens forsidekategori.
- GET /api/categories giver shoppens online kategorier, og GET /api/categories/{id} én kategori.
- GET /api/search?q= bruger samme søgning som shoppen og giver højst 250 varer.
En vare har bl.a. price (inkl. moms), old_price, buyable, images med fem formater (original, large, medium, small, og thumbnail), attributes, variants, og meta med titel og beskrivelse. Har varen pant, står den i deposit med navn og beløb pr. stk. inkl. moms, ellers er feltet null. Navne og tekster følger landets sprog.
variants har kun de varianter, der kan købes. Hver variant har attributes som attribut-id → værdi-id, og det objekt sender du med, når varen lægges i kurven. En variants price er null, når varianten ikke har sin egen pris. Felter, kunden selv skal udfylde (fx en tekst til gravering), står i attributes med input_mode: "customer".
Sider og URL'er med resolve
Med GET /api/resolve/{sti} kan din butiksfront bruge shoppens egne adresser. Du sender den sti, kunden har åbnet, og får at vide, hvad den er. Forsiden er GET /api/resolve/. Feltet type er en af disse:
- home: forsiden. Varerne henter du med /api/frontpage.
- category og landing: kategorien eller landingssiden med products og pagination, som er null, når der kun er én side. Første side er ?p=0, næste ?p=1, og sidestørrelsen er shoppens (på landingssider landingssidens egen).
- product, page, og blog: varen, den statiske side, eller blogindlægget. blog_list er blogoversigten på /blog.
- redirect: en af shoppens omdirigeringer fra Webshop → Redirects. Send kunden videre til data.url, så gamle links bliver ved med at virke.
- not_found: siden findes ikke. Svaret kommer med HTTP 200, så din butiksfront skal selv vise en 404-side og svare med status 404, hvis den renderer på serveren.
Svaret har også blocks med Sidedesignerens blokke for siden som færdig HTML, delt i top og bottom. HTML'en er renderet med shoppens aktive temas skabeloner og bruger dets CSS-klasser. Feltet er null, når siden ikke har blokke, eller temaet ikke har Sidedesigneren, og på kategorier og landingssider kommer blokkene kun på første side. Har du allerede blokkene fra serveren, så tilføj ?blocks=0. Læs om blokkene i artiklen Sidedesigner.
Navigationen bygger du ud fra GET /api/menus, så shopejeren selv kan styre den under Indhold → Menuer. Hver menu har location: top, bottom, left, eller right, som svarer til Øverst, Nederst, Venstre side, og Højre side i feltet "Placering af menuen". Kun menuer med en placering er med, og har ingen menu placeringen Øverst, får du shoppens første menu som top. Et punkt med type: "menu" er en overskrift for en undermenu uden adresse, og external fortæller, om linket går til et andet domæne. Se Menuer og navigation.
Kurv og rabatkoder
- POST /api/basket med product_id, amount (1-100), og attributes lægger en vare i kurven og svarer med hele kurven. Første kald opretter kurven og sætter cookien.
- PUT /api/basket/{line_id} med amount (0 eller mere) retter antallet, og 0 fjerner linjen. DELETE /api/basket/{line_id} fjerner den.
- POST /api/voucher/apply med code lægger en rabatkode eller et gavekort på, og POST /api/voucher/remove fjerner koden. Svaret har discount og den opdaterede kurv i basket.
Brug altid linjens line_id fra kurven, når du retter eller fjerner, fx 232156:4301+33385 for en variant. Behandl den som en tekst, du ikke selv bygger, og send den gennem encodeURIComponent(). Et rent produkt-id rammer den første linje med varen, og det kan være den forkerte variant. En linje, der ikke findes, giver kurven uændret tilbage og ingen fejl.
Vis beløbene, som API'et returnerer dem, og læg ikke selv linjerne sammen. Beløbene er shoppens egen beregning med kampagner, rabatkode, mængderabat, fragt, og landets valuta, og subtotal er varerne, efter rabatterne er trukket fra. Har kurven pant, kommer deposits og deposit_total med. Pant indgår i total og vat, men ikke i subtotal, så vis en linje med pant, ellers går tallene ikke op. Før fragttrinnet er shipping shoppens standardfragt, og shipping_chosen er false. Har kurven en kampagne eller en rabatkode, kommer campaign og voucher med, og rabatten i dem er allerede trukket fra i subtotal.
En rabatkode, der ikke findes eller ikke kan bruges, fjerner også en kode, der allerede lå på kurven. En gyldig kode kan give discount: 0, fx når den kun gælder over et bestemt beløb. Den bliver liggende og giver rabat, når kurven opfylder betingelsen. Se Opret en rabatkode.
Betal med point. Har shoppen et loyalitetsprogram, har kurven feltet points: balance er kundens saldo, available er de point, kurven kan bære efter shoppens regler, used og discount er de brugte point og deres værdi, earns er de point, ordren optjener, og rate er fx 10 point = 1 kr. For en gæst er balance og available null. POST /api/points/apply med points bruger point på ordren og kræver login, og POST /api/points/remove fjerner dem igen. For mange point giver 400 points_invalid og fjerner også de point, der lå på kurven. En rabatkode kan kun bruges sammen med point, hvis koden tillader det, ellers er available 0.
Pointrabatten er trukket fra total og vat, så vis den som en egen linje. Inden ordren oprettes, klemmes pointene igen til det, kurven kan bære, og ordren trækker dem fra kundens saldo. Optjeningen sker på ordren som i shoppens egne temaer, når ordren godkendes eller sendes. På ordren står de brugte point i points.used, rabatten i totals.point_discount, og points.earned er de point, ordren har givet indtil nu.
Checkout trin for trin
GET /api/checkout giver kurven, fragtmetoderne, betalingsmetoderne, og landene i ét kald, plus de adresser, kunden allerede har udfyldt. Der er kun de metoder med, kunden faktisk kan vælge. Selve checkout er POST /api/checkout med et step:
- address: email, phone, name, address, zipcode, city, country_id, og evt. firmafelter, kommentar, nyhedsbrev, og en anden leveringsadresse med delivery_-felterne. Svaret har missing_fields og delivers_to_country, så du kan vise fejl med det samme. Ordren afvises først, når den gennemføres. Et country_id, der ikke er et af shoppens lande, giver ingen fejl, men shoppens standardland.
- shipping: shipping_id og, for pakkeshop-metoder, delivery_shop_id (pakkeshoppens number fra delivery_shops). En metode, der ikke er på listen, afvises. Sender du ingen pakkeshop, vælger shoppen den første på listen, ligesom shoppens egen checkout gør.
- payment: payment_gateway_id. Kun metoder, der er online og må bruges i kundens land.
- complete: gennemfører ordren, se næste afsnit. Du kan også kalde POST /api/checkout/complete.
Det er der, de fleste fejl opstår:
- Send altid step, og send hele adressen hver gang. Adressetrinnet erstatter alle felter med det, du sender, også dem du udelader, og et kald uden step bliver behandlet som adressetrinnet. Er kunden logget ind, bruger checkout altid kontoens adresse, og adressetrinnet gemmer først en ny adresse på kontoen, når missing_fields er tom.
- Hent checkout igen efter adressen. Pakkeshops slås op ud fra leveringsadressen og postnummeret, og betalingsmetoderne filtreres efter fakturalandet. Før adressetrinnet er delivery_shops tom for en gæst, og for en indlogget kunde passer listen til kontoens adresse. Er listen stadig tom bagefter, fordi fragtfirmaets opslag fejler, kan metoden vælges uden pakkeshop.
- Spring ikke fragttrinnet over. Uden det bruges shoppens standardfragt og den første pakkeshop, ikke det kunden ville have valgt. Skifter kunden leveringsland i adressetrinnet, nulstilles den valgte fragt og pakkeshop, så kunden skal vælge igen.
- Typerne er ikke ens. shipping_methods[].id og kurvens product_id er tekster, mens payment_methods[].id og varens id er tal. Kurvens shipping_id er derimod et tal, så sammenlign med String().
- Betingelser og samtykke er din opgave. API'et tjekker ikke, at kunden har accepteret handelsbetingelserne. Hent dem med GET /api/settings (terms), og vis dem i din checkout. Varer med withdrawal_exempt_reason: "digital_content" kræver kundens udtrykkelige samtykke, som din checkout også selv skal indhente.
Betaling og kvittering
Send step: "complete" med payment_flow: "storefront". Betalingsmetoden er den fra betalingstrinnet, men du kan også sende den med som payment_id. Inden ordren oprettes, tjekker API'et det samme som shoppens egen checkout: lager, mindstebeløb, leveringsland, adresse, fragt, og betalingsmetode. Der kommer to slags svar:
- Betaling: payment_redirect: true og payment_url. Med storefront-flowet er det shoppens egen betalingsside /payment, som sender videre til betalingsvinduet. Det virker med alle shoppens betalingsmetoder, og en gratis ordre går samme vej. Betalingssiden bruger temaets skabelon payment.html, så bygger du dit eget tema, skal den findes.
- Ordren er oprettet: ved betalingsmetoder uden onlinebetaling, fx bankoverførsel og faktura. Svaret har redirect til kvitteringen, webshop_order_id (ordrenummeret, kunden ser), chk, og payment_info med shoppens tekst til metoden, fx kontonummeret.
Begge adresser peger på shoppens domæne, så send kunden videre med payment_url eller redirect. Uden payment_flow giver API'et selv betalingsvinduets adresse for QuickPay, OnPay, Pensopay, Flatpay, Stripe, Mollie, og Nets Easy, mens andre metoder stadig går via /payment. Kan betalingsvinduet ikke startes i det flow, svarer API'et med 502 og payment_link_failed, og der oprettes ingen ordre. Se Hvordan modtager man betaling online? og betalingsvindue i ordbogen.
Har shoppen slået "Hold kurven åben indtil ordren er betalt?" fra under Webshop → Generelt, opretter storefront-flowet ordren før betalingen, ligesom shoppens egen checkout. Så er payment_url ordrens betalingsside, og ordrenummeret er med i svaret. Se Hvad er forskellen på åben kurv og lukket kurv?
Dobbeltklik giver ikke to ordrer. Er kurven allerede blevet til en ordre, giver et nyt complete samme svar som første gang. Kommer et kald, mens ordren oprettes, venter det op til ca. 6 sekunder og får så samme svar. Først hvis ordren ikke er klar inden da, svarer API'et med 409 og order_in_progress. Lås alligevel knappen, mens kaldet kører. Ved kortbetaling med åben kurv oprettes ordren først efter betalingen, så et nyt complete giver bare et nyt link.
Kvitteringen. Efter betalingen lander kunden på shoppens /thanks med basket_id eller order_id og chk i adressen. Siden finder selv ordren og giver den til temaets skabelon thanks.html, som Washington bruger. Vil du hente ordren i din app, så brug GET /api/order med de samme parametre. Ved demobetaling og PayPal står kundens e-mail i email i stedet for chk, så brug skabelonen dér. chk er en MD5-hash af ordrens e-mail, så ordren kun vises sammen med den rigtige e-mail. Svaret har de priser, kunden betalte, og order_id er her ordrenummeret. Har ordren pant, står beløbet i totals.deposit. Modsat kurven er pant her allerede med i subtotal, og pantlinjerne står ikke i products. Kommer kunden til kvitteringen, før betalingsudbyderen har meldt betalingen, kan paid stå som false et øjeblik. Det betyder ikke, at betalingen er fejlet.
Hele købet i ét eksempel
Eksemplet kører på shoppens eget domæne. Det lægger en vare i kurven, udfylder adressen, vælger levering og betaling, gennemfører med storefront-flowet, og sender kunden videre. I en rigtig butiksfront vælger kunden selv variant, fragt, pakkeshop, og betalingsmetode.
// Kører på shoppens eget domæne. I et land med egen sti: '/en/api'
const API = '/api';
async function api(path, body) {
const res = await fetch(API + path, {
method: body ? 'POST' : 'GET',
credentials: 'include',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: body ? JSON.stringify(body) : undefined
});
const text = await res.text();
let json = null;
try { json = JSON.parse(text); } catch (e) {}
if (json && json.success) return json.data;
const error = new Error(json ? json.error.message : 'HTTP ' + res.status);
error.status = res.status;
error.reason = json ? json.error.details?.reason : 'no_json';
error.details = json ? json.error.details : null;
throw error;
}
async function buy(productId) {
// 1. Varen og en variant, der kan købes
const product = await api('/products/' + productId);
const variant = product.variants[0];
await api('/basket', { product_id: product.id, amount: 1, attributes: variant ? variant.attributes : {} });
// 2. Adressen. Landets id er shoppens eget, så slå det op
const { countries } = await api('/countries');
const denmark = countries.find(function (c) { return c.code === 'DK'; });
const address = await api('/checkout', {
step: 'address', email: 'kunde@example.com', phone: '12345678', name: 'Karen Holm',
address: 'Vestergade 12', zipcode: '8000', city: 'Aarhus C', country_id: denmark.id, newsletter: 0
});
if (address.missing_fields.length || !address.delivers_to_country) return address;
// 3. Hent fragt og betaling igen EFTER adressen (pakkeshops for postnummeret)
const checkout = await api('/checkout');
const shipping = checkout.shipping_methods[0];
const shippingStep = { step: 'shipping', shipping_id: shipping.id };
if (shipping.delivery_shops.length) shippingStep.delivery_shop_id = shipping.delivery_shops[0].number;
await api('/checkout', shippingStep);
// 4. Betalingsmetoden
await api('/checkout', { step: 'payment', payment_gateway_id: checkout.payment_methods[0].id });
// 5. Gennemfør. Lås knappen, mens kaldet kører
const result = await api('/checkout', { step: 'complete', payment_flow: 'storefront' });
// Betaling: shoppens /payment. Bankoverførsel og faktura: kvitteringen
window.location.href = result.payment_url || result.redirect;
}
Går noget galt, kaster api() en fejl med reason, så du kan sende kunden tilbage til det rigtige trin: shipping_invalid til levering, address_missing og delivery_country til adressen, payment_missing og payment_not_available til betalingen, og stock, basket_unavailable, eller min_order_value til kurven. Ved order_in_progress venter du et øjeblik og kalder complete igen.
Kundekonto og login
- POST /api/auth med email og password logger kunden ind og sætter den samme cookie som shoppens eget login. Kunden er altså logget ind i både din butiksfront og shoppen. Forkert login giver 401 og login_failed.
- POST /api/auth/logout logger kunden ud.
- GET /api/customer giver den indloggede kunde med navn, adresse, kundegruppe, pointsaldo (points), og links til kundesiderne, eller customer: null.
- GET /api/orders giver kundens ordrer, og GET /api/orders/{ordrenummer} én ordre med samme felter som /api/order. Uden login svarer de med 401 og login_required.
Logget ind får kunden sine kundegruppepriser, sin adresse udfyldt, og de betalingsmetoder, der kun er for indloggede kunder. Det er metoder med "Marker automatisk nye ordrer som betalte?", fx faktura til firmakunder. Adressetrinnet gemmer en komplet adresse på kundens konto, og en indlogget kunde handler altid med kontoens e-mail, også hvis du sender en anden i adressetrinnet. Se Kundegrupper og B2B-priser.
Opret konto, glemt adgangskode, og ret oplysninger findes ikke i API'et. Link i stedet til shoppens egne sider /user-sign-up, /user-reset-password, og /user-edit. Så får kunden spamtjek, velkomstmail, point for oprettelse, og kontrol af momsnummeret som i resten af shoppen. Bygger du et tema, skal de sider findes som skabeloner. Washington har dem.
Washington som eksempel
Washington er et af Shoporamas temaer, og det er bygget helt på det headless API. Hele butikken er ét JavaScript-program uden frameworks, og temaet er et godt udgangspunkt, hvis du vil bygge din egen butiksfront som et tema. Du kan downloade det fra temasiden for Washington, og opsætningen for butiksejeren står i Washington-temaet. Temafilerne lægger du op under Design → SFTP-adgang eller retter under Design → Kodeeditor. Se Log på med SFTP og Kodeeditoren.
Washington viser også, hvad en egen butiksfront selv skal sørge for:
- SEO: Washington renderer første sidevisning på serveren med Sidedesignerens blokke, titel, meta description, canonical, Open Graph, og produktdata (JSON-LD). Varelister og produktsider bygges i browseren. Se Implementér strukturerede data i dit Shoporama-tema og JSON-LD i ordbogen.
- Sporing: Washington sender GA4-hændelserne (fx view_item, add_to_cart, begin_checkout, og purchase) til dataLayer, og ved hvert sideskift virtual_page_view med page_location, page_path, og page_title. Vil din butiksfront måle sidevisninger og køb, skal den selv sende dem. Se Implementer tracking i et Shoporama-tema og dataLayer i ordbogen.
- Scripts fra shoppen: I Washington kører apps og egne scripts fra shoppens HTML-felter kun ved første sideindlæsning, fordi siden ikke genindlæses ved sideskift. Det samme gælder for din egen app, hvis den skifter side på samme måde.
- Shoppens egne sider: Washington har skabeloner til betalingssiden, kvitteringen, kundesiderne, og fortrydelsen (/withdrawal), så de kører på shoppens domæne i samme design.
Ofte stillede spørgsmål
Skal jeg have en API-nøgle for at komme i gang?
Nej. Åbn https://din-shop.dk/api/products?limit=5 i browseren, så ser du shoppens varer. API'et kan kun det samme som en kunde i shoppen: se online varer og sider og arbejde med sin egen kurv, konto, og egne ordrer. Det kan ikke ændre varer, priser, eller andre kunders ordrer, og metoder, der markerer ordren som betalt, tilbydes kun indloggede kunder. Til alt andet bruger du REST API'et med en nøgle.
Kan butiksfronten ligge på et andet domæne end shoppen, fx hos Vercel?
Kun for katalog og indhold. Kurv, checkout, og login kræver kundens cookies, og dem sender browseren kun, når siden vises fra shoppens primære domæne (Webshop → Domæne) og kalder API'et på samme domæne. Kører du kaldene gennem din egen server, ligger sessionen på din server og ikke hos kunden, og så kan shoppens betalingsside ikke finde kurven. Betalingen og kvitteringen på /thanks foregår under alle omstændigheder på shoppens domæne. Den sikre løsning er at bygge butiksfronten som et tema.
Gælder grænsen på 300 kald i minuttet, når min server renderer siderne?
Der er to grænser. API'ets egen grænse på 300 kald i minuttet tælles i sessionen (sessionscookien) og pr. IP-adresse, så kald uden cookies får en ny session hver gang og påvirker ikke kundens. Shoppens grænse pr. IP-adresse tæller derimod alle forespørgsler fra din server, med og uden cookies, og en server, der henter mange sider, kan ramme den. Så afvises serverens IP-adresse i en time. Cache derfor katalog og indhold på din server i stedet for at hente dem ved hver sidevisning, send en User-Agent med, og lad kurv og checkout gå fra kundens browser.
Kan jeg route ud fra shoppens adresser og bruge Sidedesignerens blokke i mine egne komponenter?
Ja. Send stien til /api/resolve, så får du typen, også redirect for shoppens omdirigeringer. Blokkene er almindelig HTML, du kan sætte ind i en komponent, men de er renderet med shoppens aktive tema og bruger dets CSS-klasser, så du skal have temaets CSS med eller style klasserne selv. Billederne i blokkene har relative adresser (/cache/...), mens linkene er fulde adresser (https://din-shop.dk/...), så fang dem selv, hvis din app skifter side uden at genindlæse. Scripts i blokkene kører ikke, når HTML'en sættes ind med innerHTML. Har det aktive tema ikke Sidedesigneren, er blocks altid null.
Kan et dobbeltklik på "Gennemfør køb" give to ordrer eller en ubetalt ordre?
Ikke to ordrer. Et nyt complete for samme kurv giver samme svar som det første og laver ingen ny ordre. En ubetalt ordre opstår kun ved betalingsmetoder uden onlinebetaling, som bankoverførsel og faktura, og når shoppen bruger lukket kurv, ligesom i shoppens egen checkout. Kan betalingsvinduet ikke startes, oprettes der ingen ordre. Lås alligevel knappen, mens kaldet kører.
Kan jeg teste hele købet uden at lave rigtige ordrer?
Alle kald før complete ændrer kun kurven og sessionen (og kundens konto, hvis du er logget ind), så dem kan du køre så tit, du vil. Det er complete, der opretter ordren, og den går gennem shoppens almindelige flow med ordrebekræftelse, webhooks, og evt. økonomisystem. Test derfor med en betalingsmetode uden onlinebetaling, som bankoverførsel, og din egen e-mail, og slet bagefter testordren med Slet ordre på ordren under Ordrer. Er shoppen stadig i demo, kører betalingssiden i testtilstand, så du også kan prøve betaling med kort.
Mister shoppen placeringer på Google med en egen butiksfront?
Det afhænger af butiksfronten. API'et giver dig shoppens egne adresser og omdirigeringer via resolve og titel og beskrivelse i meta på varer, kategorier, og sider, så du kan beholde de samme URL'er og tekster. Men det er din butiksfront, der skal sætte titel, meta description, canonical, og strukturerede data i HTML'en, helst allerede fra serveren, som Washington gør. Aftal det med den, der bygger butiksfronten.
Kan jeg stadig bruge mine betalingsmetoder, pakkeshops, rabatkoder, point, og kundekonti?
Ja. Storefront-flowet virker med alle shoppens betalingsmetoder, pakkeshops vælges i fragttrinnet, rabatkoder og gavekort lægges på med /api/voucher/apply, og kunderne logger ind med deres eksisterende login. Kunderne kan også betale med point fra loyalitetsprogrammet med /api/points/apply. API'et har ingen understøttelse af abonnementer, ønskelister, produktfiltre, anmeldelser, tilkøb, og fil-upload i kundefelter.
Hvordan får jeg ordren ind i mit eget system?
Headless API'et viser kun kunden sine egne ordrer. Opret i stedet en webhook under Integrationer → Webhooks, fx på hændelserne order_created og paid, så kalder shoppen din server. Skal dit system hente eller rette ordrer, så brug REST API'et fra din server. Se Webhooks og webhook i ordbogen.
Alle endpoints, felter, og eksempler på svar finder du i referencen til det headless API. Har du spørgsmål, så skriv til support@shoporama.dk.
Related articles
REST API
Complete guide to Shoporama's REST API: authentication, all endpoints, examples and Swagger documentation.
OAuth - give apps access to your webshop
Learn how OAuth works in Shoporama. Give third-party apps secure access to your online store without sharing your password.
Page designer
Lær hvordan du bruger Sidedesigner i Shoporama til at opbygge sidelayouts med blokke som hero-sektioner, produktlister, bannere og meget mere, helt...
Webhooks in Shoporama
Complete guide to webhooks in Shoporama. Get automatically notified on a URL when something happens in your online store. List of all events,...
Move your shop to your own domain
Guide to moving your Shoporama online store to your own domain with DNS setup, SSL and domain validation.
Prepare your theme for multiple languages and countries
Step-by-Step Guide for Developers: How to Make Your Own Shoporama Theme Work with Countries, Including Translated Texts, the Country's Currency and...
Related features
Headless Commerce and OAuth
Use Shoporama as a headless backend with OAuth login and a comprehensive REST API. Build custom frontends, apps and integrations.
REST API - build exactly the integration you want
Full REST API with access to products, orders, customers and more. Build your own integrations, apps or a headless frontend.
Headless commerce
Headless commerce separates frontend and backend via APIs. Learn what it is, the pros and cons, and when it makes sense for your webshop.
REST API
REST API is a standard for system communication over HTTP. Read about REST APIs, integrations and how webshops use them.
Webhook
A webhook automatically sends messages between systems when events occur. Read about webhooks, integrations and real-time communication in eCommerce.