nordkit/svea
Modern PHP SDK for Svea Checkout, Payment Admin, webhook subscriptions and inbound webhook verification. Fluent API with typed value objects, retries, idempotency, async task polling, and a robust testing fake. Includes first-class Laravel integration.
The Checkout API creates the customer-facing order, returns a JavaScript snippet to embed, and lets you fetch / update / cancel it.
::: tip Source of truth Until the docs site grows, the canonical reference for every field, optional flag, and edge case is the README — Checkout. This page summarises the API surface. :::
| Method | Returns | Notes |
|---|---|---|
Svea::checkout()->create($order) |
CheckoutResponse |
Accepts a CheckoutOrder instance or a fluent callback (see below). |
Svea::checkout()->get($orderId) |
CheckoutResponse |
Fetch the current state. |
Svea::checkout()->update($orderId, $order) |
CheckoutResponse |
Same input shape as create(). |
Svea::checkout()->cancel($orderId) |
void |
Every checkout order requires a matching countryCode, currency, and locale. These three parameters identify the merchant market and determine which payment methods and checkout UI language Svea presents to the customer.
| Market | Country code | Currency | Locale |
|---|---|---|---|
| Sweden | SE |
SEK |
sv-SE |
| Norway | NO |
NOK |
nn-NO |
| Denmark | DK |
DKK |
da-DK |
| Finland | FI |
EUR |
fi-FI |
Additional supported locales for international checkouts: de-DE, en-US.
Every method that accepts a CheckoutOrder supports two equivalent input styles. Use whichever fits the situation.
Best when all the data is known upfront and you want a single statement:
use Svea\Checkout\Cart;
use Svea\Checkout\CheckoutOrder;
use Svea\Checkout\MerchantSettings;
use Svea\Checkout\OrderRow;
$order = Svea::checkout()->create(new CheckoutOrder(
currency: 'SEK',
countryCode: 'SE',
locale: 'sv-SE',
clientOrderNumber: 'ORD-001',
merchantSettings: new MerchantSettings(
pushUri: route('webhooks.svea'),
termsUri: route('terms'),
confirmationUri: route('checkout.confirmation'),
checkoutUri: route('checkout'),
),
cart: new Cart([
new OrderRow(quantity: 100, unitPrice: 29900, vatPercent: 2500, sku: 'TSHIRT-BLK-M', name: 'T-Shirt Black M'),
]),
));
Best when you need loops, conditionals, or to compose the order across multiple methods. Pass a closure that receives a pre-constructed CheckoutOrder and chain setters on it:
$order = Svea::checkout()->create(function (CheckoutOrder $order) use ($cart) {
$order
->currency('SEK')
->countryCode('SE')
->locale('sv-SE')
->clientOrderNumber($cart->reference)
->merchantSettings(fn (MerchantSettings $s) => $s
->pushUri(route('webhooks.svea'))
->termsUri(route('terms'))
->confirmationUri(route('checkout.confirmation'))
->checkoutUri(route('checkout')));
foreach ($cart->items as $item) {
$order->addRow(fn (OrderRow $row) => $row
->sku($item->sku)
->name($item->name)
->quantity($item->qty * 100) // minor units: 100 = 1 unit
->unitPrice($item->unit_price) // incl. VAT, minor currency (öre)
->vatPercent($item->vat_percent) // 2500 = 25%
->unit('st'));
}
// Conditional rows — see Conditionable below
$order->when($cart->has_discount, fn ($o) => $o->addRow(
fn (OrderRow $r) => $r->sku('DISC')->name('Discount')->unitPrice(-500)->quantity(100)->vatPercent(2500)
));
});
Both forms produce identical HTTP requests. Mix freely across create(), update(), and tests.
when() / unless()Every builder (CheckoutOrder, MerchantSettings, OrderRow, AdminOrderRequest, AdminOrderRow, CreditRequest) uses the Conditionable trait. Inline branching without breaking the chain:
Svea::checkout()->create(function (CheckoutOrder $order) use ($cart, $isB2B) {
$order
->currency('SEK')
->locale('sv-SE')
->when($isB2B, fn ($o) => $o->requireElectronicIdAuthentication())
->unless($isB2B, fn ($o) => $o->metadata(['segment' => 'consumer']))
->when(
$cart->has_promo,
fn ($o) => $o->merchantData("promo:{$cart->promo_code}"),
fn ($o) => $o->merchantData('no-promo'), // optional else branch
);
});
when($condition, $then, $else = null) calls $then($builder) if the condition is truthy, otherwise the optional $else($builder). unless() is the inverse. Both return the builder so chaining continues seamlessly.
$order = Svea::checkout()->get('12345678');
$order->id(); // '12345678'
$order->status(); // 'Created' | 'Final' | 'Cancelled'
$order->snippet(); // '<script>...</script>'
$order->successful();
$order->getLastResponse()->statusCode;
CheckoutOrder — currency(), locale(), countryCode(), clientOrderNumber(), merchantSettings(), addRow(), plus optional merchantData(), partnerKey(), recurring(), requireElectronicIdAuthentication(), metadata()MerchantSettings — pushUri(), termsUri(), confirmationUri(), checkoutUri()OrderRow — sku(), name(), quantity(), unitPrice(), vatPercent(), discountPercent(), unit()All builders use the minor-unit convention (100 = 1 unit, 2500 = 25%).
How can I help you explore Laravel packages today?