Developers Guides Create an Order

Create an Order

Learn how to create orders with line items, tax, customer details, and control functions. The starting point for accepting payments with the Surfboard API.

Online API Orders In-Store

Overview

An order is the starting point for every payment in Surfboard. You create an order against a terminal$id, include line items with pricing, and optionally initiate payment in the same call. The API returns an orderId and paymentId that you use for all subsequent operations.

This guide covers basic order creation, line items, customer details, tax handling, and common control functions.

Prerequisites

  1. Create a developer account at the Developer Portal
  2. Complete onboarding (merchant and store setup)
  3. Register a terminal (any type — in-store, PaymentPage, SelfHostedPage, or MerchantInitiated)

Basic Order

Create an order with a single line item and initiate payment:

POST /merchants/:merchantId/orders
{
  "terminal$id": "YOUR_TERMINAL_ID",
  "orderLines": [
    {
      "id": "ITEM-001",
      "name": "Nike Shoes",
      "quantity": 1,
      "amount": {
        "regular": 50000,
        "total": 50000,
        "currency": "752",
        "tax": [
          { "amount": 10000, "percentage": 25, "type": "VAT" }
        ]
      }
    }
  ],
  "totalOrderAmount": {
    "regular": 50000,
    "total": 50000,
    "currency": "752",
    "tax": [
      { "amount": 10000, "percentage": 25, "type": "VAT" }
    ]
  },
  "controlFunctions": {
    "initiatePaymentsOptions": {
      "paymentMethod": "CARD"
    }
  }
}
// Response
{
  "status": "SUCCESS",
  "data": {
    "orderId": "83a1ba32774149710b",
    "paymentId": "83a1ba3264bd500106"
  },
  "message": "Order created successfully"
}

Store both orderId and paymentId — you need them for status checks, captures, voids, and refunds.

Line Items

Every order requires at least one line item in the orderLines array. Each line item must include:

FieldRequiredDescription
idYesUnique line item identifier
nameYesProduct name
quantityYesQuantity (negative for refunds)
amount.regularYesUnit price in smallest currency unit
amount.totalYesTotal line amount in smallest currency unit
amount.currencyYesNumeric ISO 4217 code (e.g., "752" for SEK)

Optional fields include description, brand, imageUrl, gtin, categoryId, unit, and metadata.

Currency format: All amounts use the smallest currency unit. For example, 10.00 SEK = 1000, 5.00 EUR = 500.

Customer, Billing, and Shipping

Include customer, billing, and shipping details when available:

{
  "terminal$id": "YOUR_TERMINAL_ID",
  "customer": {
    "person": {
      "name": { "firstName": "John", "lastName": "Doe" },
      "email": "john@example.com",
      "phoneNumber": { "code": "46", "number": "768100190" }
    },
    "company": {
      "vatId": "SE556026998601"
    }
  },
  "billing": {
    "name": { "firstName": "John", "lastName": "Doe" },
    "phoneNumber": { "code": "46", "number": "768100190" },
    "address": {
      "addressLine1": "Storgatan 1",
      "city": "Stockholm",
      "postalCode": "11122",
      "countryCode": "SE"
    }
  },
  "shipping": {
    "name": { "firstName": "John", "lastName": "Doe" },
    "phoneNumber": { "code": "46", "number": "768100190" },
    "address": {
      "addressLine1": "Storgatan 1",
      "city": "Stockholm",
      "postalCode": "11122",
      "countryCode": "SE"
    }
  },
  "orderLines": [...]
}

All customer fields are optional but recommended for invoice payments, fraud prevention, and receipt delivery.

Order Line Level Calculation

The orderLineLevelCalculation control function changes how totalOrderAmount is computed from line items.

SettingFormulaExample
false (default)Sum of (total * quantity) per line(50 * 2) + (150 * 1) = 250
true (recommended)Sum of ((regular * quantity) - campaign + shipping) per line((200 * 2) - 100 + 50) = 350

Enable it when your line items have campaigns or shipping costs:

{
  "controlFunctions": {
    "orderLineLevelCalculation": true,
    "initiatePaymentsOptions": { "paymentMethod": "CARD" }
  }
}

Adjustments

Adjustments modify the total order value for tips, donations, gift cards, or discounts:

{
  "terminal$id": "YOUR_TERMINAL_ID",
  "orderLines": [...],
  "adjustments": [
    { "type": "TIP", "value": 1000 }
  ],
  "totalOrderAmount": {
    "regular": 50000,
    "total": 51000,
    "currency": "752"
  },
  "controlFunctions": {
    "initiatePaymentsOptions": { "paymentMethod": "CARD" }
  }
}

The totalOrderAmount.total should reflect the adjusted amount (regular + adjustments).

Delay Capture

To authorize payment now but capture funds later (e.g., at shipment), set delayCapture: true:

{
  "controlFunctions": {
    "delayCapture": true,
    "initiatePaymentsOptions": { "paymentMethod": "CARD" }
  }
}

You can also use authMode: "PRE-AUTH" for pre-authorization flows, which automatically enables delayed capture and lets you capture a different amount than originally authorized.

See the Capture a Payment guide for the full flow.

Check Order Status

After creating an order, check its status at any time:

GET /merchants/:merchantId/orders/:orderId/status
// Response
{
  "status": "SUCCESS",
  "data": {
    "orderStatus": "PAYMENT_COMPLETED",
    "payments": [
      {
        "paymentId": "83a1ba3264bd500106",
        "paymentStatus": "PAYMENT_COMPLETED",
        "paymentMethod": "CARD",
        "amount": 50000
      }
    ],
    "paymentIds": ["83a1ba3264bd500106"]
  }
}

Order statuses: PENDING | PAYMENT_COMPLETED | PAYMENT_CANCELLED | PARTIAL_PAYMENT_COMPLETED | PAYMENT_PROCESSED

Payment statuses: PAYMENT_INITIATED | PAYMENT_PROCESSING | PAYMENT_PROCESSED | PAYMENT_COMPLETED | PAYMENT_FAILED | PAYMENT_CANCELLED

Every payment ends in one of three terminal states:

Payment StatusOrder StatusDescription
PAYMENT_COMPLETEDPAYMENT_COMPLETEDPayment succeeded — the order is closed.
PAYMENT_CANCELLEDPENDINGPayment was cancelled — the order remains open and a new payment can be initiated using the existing orderId.
PAYMENT_FAILEDPENDINGPayment failed — the order remains open and a new payment can be initiated using the existing orderId.

Error Handling

Create order responses return status: "ERROR" with a code in the OR_*, PS_*, GC_*, or SP_* prefix when validation or initiation fails. The most common ones are OR_0042 (terminal not found), OR_0037 (invalid total), OR_0048 (mixed currencies), and PS_0025 (terminal not connected — retry after configure).

See the Create Order Error Codes reference for the full list, including errors thrown by the initiate payment step when both happen in the same call.

Next Steps

Once you have an order created, you can:

Reference

Ready to get started?

Create a sandbox account and start building your integration today.