Start an order, take a PIN terminal payment and invoice the items#
This guide walks through the exact flow the Afosto POS uses to ring up a sale: start an order, add items, route the payment to a PIN terminal, invoice the items and finish the order.
Overview#
- Start an order using
createOrder - Add items via
addItemsToOrder - List the available payment methods and terminals via the
orderquery - Attach the PIN payment method and terminal with
addPaymentMethodToOrder - Create a payment for the amount due (REST)
- Start the terminal transaction via the payment's
action.urland wait for the result - Finish the order with
openOrder - Generate the invoice with
invoiceItems
All GraphQL operations are sent to https://afosto.app/graphql with a Bearer token in the Authorization header. The two REST calls in steps 5 and 6 use the same host and token.
Step 1 — Start an order#
Orders start in the CONCEPT state: you can freely add items, customers and payment details until the order is opened in step 7.
Input: CreateOrderInput!
| Name | Type | Required | Description |
|---|---|---|---|
channel_id | String | Optional | The channel to create the order in. Required when not authenticated with a storefront token. |
currency | Currency▾ | Optional | The currency for the order, e.g. EUR. |
customer | CustomerInput▾ | Optional | Attach a contact or organisation to the order. |
Returns: Order
| Name | Type | Required | Description |
|---|---|---|---|
id | ID! | Required | The ID |
number | String! | Required | Order number |
total | Money! | Required | Total value |
currency | Currency! | Required | Currency code |
Save the returned order.id — every following step needs it.
Step 2 — Add items#
Input: AddItemsToOrderInput!
| Name | Type | Required | Description |
|---|---|---|---|
order_id | String! | Required | The order to add items to. |
items | [OrderItemInput!]!▾ | Required | The items to add. Pass the sku and quantity; pricing is resolved from your catalog. |
Returns: Order
| Name | Type | Required | Description |
|---|---|---|---|
id | ID! | Required | The ID |
number | String! | Required | Order number |
total | Money! | Required | Total value |
currency | Currency! | Required | Currency code |
Each order line exposes ids — one ID per unit. The invoice in step 8 references these item IDs,
but you don't need to track them yourself: the order's actions field tells you which IDs are
ready to invoice.
Step 3 — List payment methods and terminals#
The payment options for an order — including the PIN terminals configured for your tenant — are exposed on the order itself:
The PIN payment method is the one that exposes terminals. Pick a terminal with is_online: true
and save both the method.id and the terminal.id for the next step.
Step 4 — Attach the PIN payment method and terminal#
Use addPaymentMethodToOrder to register the PIN payment method on the order, passing the terminal_id to route the transaction to the correct device.
Input: AddPaymentMethodToOrderInput!
| Name | Type | Required | Description |
|---|---|---|---|
order_id | String! | Required | The order ID. |
method_id | String! | Required | The payment method ID from step 3. |
terminal_id | String | Optional | The PIN terminal ID. Required for in-person PIN payments. |
issuer_id | String | Optional | The issuer ID, for issuer-based methods such as iDEAL. Not used for PIN. |
drawer_id | String | Optional | The cash drawer ID, for cash payments. Not used for PIN. |
Returns: Order
| Name | Type | Required | Description |
|---|---|---|---|
id | ID! | Required | The ID |
number | String! | Required | Order number |
total | Money! | Required | Total value |
currency | Currency! | Required | Currency code |
options.proceeding_step describes what is needed to proceed with checkout. For a PIN payment the
next action is creating and executing a payment.
Step 5 — Create the payment#
Create a payment on the order for the amount due. This is a REST call:
Returns: the payment, including its id, status (PENDING) and an action describing how to proceed. For a payment routed to a PIN terminal the action type is START:
amount may be lower than the order total — create multiple payments to support split payments.
Save the payment's action.url for the next step.
Step 6 — Start the terminal transaction and wait for the result#
Perform a GET request on the payment's action.url. This starts the transaction on the terminal — the customer is prompted to tap or insert their card — and the request stays open until the terminal reports a result, so there is no need to poll yourself:
Returns: the updated payment once the transaction settles.
Check is_paid on the response. When the customer aborts the transaction on the device, the
payment comes back unpaid (PENDING, or CANCELLED/EXPIRED) — call the action.url again to
restart the terminal transaction, or create a new payment.
The action.type depends on the payment method: START for PIN terminal payments, PAY for manual methods such as cash (mark the payment as paid yourself), and FOLLOW for external payment pages (redirect the customer to the URL).
Step 7 — Finish the order#
Once the full amount is paid, finish checkout by opening the order. This transitions the order from CONCEPT to OPEN and starts processing: stock is allocated and the items become ready to invoice.
Input: OpenOrderInput!
| Name | Type | Required | Description |
|---|---|---|---|
order_id | String! | Required | The order to open. |
Returns: Order
| Name | Type | Required | Description |
|---|---|---|---|
id | ID! | Required | The ID |
number | String! | Required | Order number |
total | Money! | Required | Total value |
currency | Currency! | Required | Currency code |
The order now starts processing — the next step waits for the items to become invoiceable.
Step 8 — Invoice the items#
After the order is opened, the order exposes an invoice action listing the item IDs that are ready to be invoiced. Processing is asynchronous, so poll the order until the action appears (for example poll every 250 ms, with a 15 second timeout):
When the action with action: "invoice" contains IDs, pass them to invoiceItems:
Input: InvoiceItemsInput!
| Name | Type | Required | Description |
|---|---|---|---|
items | [String!]! | Required | The item IDs from the order's invoice action. |
invoice_id | String | Optional | An existing invoice ID to add the items to. Omit to create a new invoice. |
Returns: InvoiceItemsPayload
| Name | Type | Required | Description |
|---|---|---|---|
invoice | Invoice▾ | Optional | The created invoice |
The order is now fully processed: paid via the PIN terminal, opened and invoiced.
Notes#
- Money values (
total,amount, item prices) are integers in cents — e.g.14994= € 149,94. - Order states are
CONCEPT→OPEN→CLOSED. The POS works on aCONCEPTorder throughout checkout and opens it only after the payment is settled. - You can invoice a subset of items by passing only some of the IDs from the
invoiceaction — useful for partial invoicing. - For cash payments the same flow applies, but pass a
drawer_idinstead of aterminal_idin step 4. The payment's action is thenPAYinstead ofSTART: mark the payment as paid viaPUT https://afosto.app/api/commerce/payments/management/{payment_id}/paywith body{ "data": { "amount": <amount> } }instead of calling a start URL. - See Authentication for how to obtain and pass your API token.