A POS with no server (station keys)
How do I integrate when my POS calls Merchant360 straight from the store?
- For
- An ISV whose POS runs on the store PC with no server of its own in between.
- Key
- One station key (m360_stn_…) per register.
POS station (station key) -> Merchant360 API -> that station's reader
result <- GET /payments/{id}Get a station key for each register
Electronic Payments does thisOnce per registerKeys are made in the ISV portal: one at a time on the merchant’s page (Station keys, Mint key), or a file of keys for a whole rollout on the Developer tab. Your own portal logins can make them. Each key is shown or downloaded once; refunds are allowed only when ticked at that moment.
Install the key at its register only
RequiredOnce per registerKeep it in the POS’s protected settings on that register. No M360-On-Behalf-Of header: the key already names the merchant and the reader, and terminal_id may be left out. Check it with GET /me: it answers type: "station" and its terminal_id.
Send the sale to the reader
RequiredEvery salePOST /payments with the amount in cents and the terminal_id of the reader at that station. Merchant360 hands the sale to that reader: it leaves its waiting screen, shows the amount and asks for the card (tap, chip or swipe). Card data never reaches your software.
Always send terminal_id: it is how Merchant360 knows which reader to wake. Left out, the sale goes to the merchant’s default reader, which is wrong for a store with two stations.
You get 202 at once, with status: "pending" and a payment id starting tp_. Save the id.
POST /payments
Authorization: Bearer m360_stn_…
Idempotency-Key: T-1001
{ "amount": 1800, "terminal_id": "PAXA35/2290000001", "invoice_number": "T-1001" }
202 { "id": "tp_5b0e…", "object": "payment", "status": "pending", "amount": 1800 }Get the result
RequiredEvery saleAsk for the payment every 2 to 3 seconds until its status is no longer pending:
succeeded: approved.amountis what was charged (with any tip or fee the merchant’s pricing adds on the reader);cardhas the brand and last 4.failed: declined.canceled: cancelled on the reader, or by you.
A sale is never cancelled on a timer. One still pending after 360 seconds is flagged for review and stays pending.
GET /payments/tp_5b0e…
200 { "id": "tp_5b0e…", "status": "succeeded", "amount": 1800,
"card": { "brand": "VISA", "last4": "4242" } }Cancel, void, refund, tip
RequiredWhen the clerk asksPOST /payments/{id}/cancel: stop a sale still waiting for the card. If the card was charged just before, it is voided.POST /payments/{id}/void: undo an approved sale the same day, before the batch closes.POST /payments/{id}/refund: money back after the batch closed. Sendamountfor part of it.POST /payments/{id}/tip-adjust: add or change a tip on an approved sale.
Handle a lost answer
RequiredEvery POSTSend an Idempotency-Key (your order or ticket id) on every POST. If the answer is lost (timeout, restart, Wi-Fi drop), send the same POST again with the same key: you get the same payment back, never a second sale. Then read its status as in the previous step.
Per-sale options
OptionalAny saleterminal_options.allow_cancel: false: no Cancel Sale button on the reader for this sale.terminal_options.print_receipt: true: the reader prints or offers the receipt for this sale, for a register with no printer.false: your POS prints and the reader goes back to its waiting screen. Left out: the terminal’s setting (Payment App 2.46.4 or later).terminal_options.allow_keyed: true: a Key enter card button, for a card that will not read. Needs keyed (MOTO) sales on the merchant; the reader then asks for CVV, ZIP and street number by default.tip: { prompt: true }: the tip screen on the reader.POST /payments/authorizewith the same body: a hold instead of a sale, captured later with/payments/{id}/captureor released with/void. Needs pre-auth on the merchant.
Know what a station key cannot do
RequiredDesignAnother reader, customers, payment lists, reports, settings, webhooks and typed card numbers are all 403 station_key_scope. Refunds only when the key allows them. 120 requests a minute per key. Results come from GET /payments/{id}: a station key cannot register webhooks.
Show the error code
RequiredAny refused callEvery error is { "error": { "message", "code" } }. Most refusals name a setting, not an outage: show the code to your user instead of “contact support”.
403 no_grant: the merchant is not connected to you.403 insufficient_scope: your key is not granted that kind of call.403 billing_tools_off: the merchant’s Billing Tools are off.403 not_permitted_by_channel: reports or refunds are off for partners on that merchant.409 terminal_unavailable: no card readers on the account.422 moto_not_enabled/422 preauth_not_enabled: keyed sales / pre-auth are off for the merchant.429 rate_limited: too many requests in the minute.500: ours. Send us the time and your reference.