Payment Flow

Authorize a Payment Order, collect the card, cancel, and chain several sales.

Take a payment

Each sale is one Payment Order:

  1. Your backend creates the Payment Order and returns its id and objectSecret.
  2. authorizePaymentOrder() prepares the payment on the phone.
  3. collectPayment() asks the customer to tap, then confirms the payment.
import { LemonwayTerminal, type LemonwayTerminalError } from '@lemonway/terminal';

async function charge(cartId: string) {
  const { id, objectSecret } = await myBackend.post('/terminal/payment-orders', { cartId });
  try {
    const authorized = await LemonwayTerminal.authorizePaymentOrder({ id, objectSecret });
    const payment = await LemonwayTerminal.collectPayment(authorized);
    showSuccess(payment.amount, payment.currency);
  } catch (error) {
    handlePaymentError(error as LemonwayTerminalError);
  }
}

The phone must be connected before collectPayment().

collectPayment() resolves a PaymentResult:

FieldDescription
paymentOrderIdThe Payment Order id (po_…).
paymentIntentIdThe payment id (pi_…).
statusPayment status, succeeded for a captured payment.
amountAmount in cents.
currencyCurrency code, lowercase (eur).
❗️

The result is for the UI

Deliver the goods or close the order from the webhook on your backend, never from collectPayment() or payment.success.


Cancel a payment

Call cancelCollectPayment() until the card is read, for example from a Cancel button:

await LemonwayTerminal.cancelCollectPayment();

collectPayment() then rejects with PAYMENT_CANCELED and the SDK emits payment.canceled. Once the card is read, the payment can no longer be canceled and cancelCollectPayment() throws.


After a failure

What to do depends on the error code:

CodeNext step
PAYMENT_DECLINED, PAYMENT_CANCELEDThe Payment Order is closed. Create a new Payment Order for the next try.
CARD_READ_ERRORThe card was not read. Ask the customer to tap again and call collectPayment() again with the same authorized object.
NETWORK_ERROR, UNKNOWNThe card may have been charged. Check the Payment Order status on your backend before creating a new one.
🚧

Avoid charging twice

A network failure during collectPayment() does not mean the payment failed. Always check the Payment Order on your backend before asking the customer to pay again.

See Error handling for every code.


Several payments

  • One payment at a time. A second collectPayment() while one is running throws PAYMENT_IN_PROGRESS.
  • Keep the phone connected between sales. Do not reconnect for each payment.
  • Safe retries. Calling authorizePaymentOrder() twice on the same pending Payment Order returns the same payment, unless its amount changed.
  • Split payments. Create one Payment Order per card and group them with your own reference.
  • Several phones. Each phone is its own reader. All phones of a shop share the same location.

Did this page help you?