Skip to content

In-Person Payments Quickstart

Pair Virtual Solo, start a sandbox Cloud API checkout, and verify your first in-person payment.

Complete your first in-person payment with the Cloud API and Virtual Solo(Opens in a new tab). This path exercises the same reader APIs as a physical Solo without requiring hardware or a native mobile application.

Expected time: 15–20 minutes after you can access the SumUp Dashboard.

Note

You are finished when Virtual Solo completes the simulated payment and Get Reader Checkout returns successful.

Surface Responsibility
Your POS backend or terminal Pairs the reader, creates the checkout, stores identifiers, and verifies the final result. API and Affiliate Keys stay here.
Virtual Solo or physical Solo Presents the amount and cardholder flow. Card data never passes through your POS backend.
SumUp Connects the checkout to the reader, processes the simulated payment, and records its status.
  • A SumUp developer account and sandbox merchant account.
  • The sandbox merchant’s merchant code, currency, and API key.
  • An Affiliate Key and its matching application ID.
  • A terminal with curl and jq.
  • A browser in which to run Virtual Solo.
  1. Log in to the SumUp Dashboard(Opens in a new tab).
  2. Open Developer Settings(Opens in a new tab).
  3. Create a sandbox merchant account, then select it in the Dashboard account switcher.
  4. With the sandbox merchant selected, go to Settings > For Developers > Toolkit.
  5. Under API Keys, create and copy an API key. Do not use the SumUp Public Key.
  6. Under Affiliate Keys, create a key for an application ID you control, such as com.example.quickstart.
  7. Copy the sandbox merchant code and note its account currency.

If you do not have a SumUp account, create a developer account(Opens in a new tab). New developer accounts start with a sandbox merchant account.

Sandbox transactions are simulations and do not move real funds.

Replace the values below. SUMUP_APP_ID must exactly match an application ID assigned to the Affiliate Key, and the currency must match the sandbox merchant account.

export SUMUP_API_KEY="sk_test_replace_me"
export SUMUP_MERCHANT_CODE="replace_me"
export SUMUP_AFFILIATE_KEY="replace_me"
export SUMUP_APP_ID="com.example.quickstart"
export SUMUP_CURRENCY="EUR"
Caution

API keys authorize account access; Affiliate Keys identify the card-present integration. Keep both on your backend and send the full affiliate object in every reader checkout.

  1. Open Virtual Solo(Opens in a new tab) and select the sandbox environment.
  2. In the simulated reader, open Connections > API > Connect.
  3. Copy the displayed pairing code. It expires after five minutes.
  4. Export the code, then create the reader:
export SUMUP_PAIRING_CODE="replace_me"
SUMUP_READER_RESPONSE="$(
curl --fail-with-body --silent --show-error \
--request POST "https://api.sumup.com/v0.1/merchants/$SUMUP_MERCHANT_CODE/readers" \
--header "Authorization: Bearer $SUMUP_API_KEY" \
--header "Content-Type: application/json" \
--data @- <<JSON
{
"pairing_code": "$SUMUP_PAIRING_CODE",
"name": "Quickstart Virtual Solo"
}
JSON
)"
echo "$SUMUP_READER_RESPONSE" | jq
export SUMUP_READER_ID="$(echo "$SUMUP_READER_RESPONSE" | jq -r '.id')"

The initial response identifies the reader:

{
"id": "rdr_3MSAFM23CK82VSTT4BN6RWSQ65",
"name": "Quickstart Virtual Solo",
"status": "processing",
"device": {
"identifier": "VIRTUAL-SOLO-01",
"model": "virtual-solo"
}
}

Wait for the pairing confirmation in Virtual Solo, then retrieve the reader:

curl --fail-with-body --silent --show-error \
"https://api.sumup.com/v0.1/merchants/$SUMUP_MERCHANT_CODE/readers/$SUMUP_READER_ID" \
--header "Authorization: Bearer $SUMUP_API_KEY" | jq

Continue when the reader status is paired. If it remains processing, wait briefly and retrieve it again. If it becomes expired, generate a new pairing code and repeat this step.

Use a unique foreign transaction ID for every attempt. For currencies with two decimal places, value: 1200 and minor_unit: 2 represent 12.00.

export SUMUP_AMOUNT_VALUE="1200"
export SUMUP_FOREIGN_TRANSACTION_ID="quickstart-$(date +%s)"
SUMUP_READER_CHECKOUT_RESPONSE="$(
curl --fail-with-body --silent --show-error \
--request POST "https://api.sumup.com/v0.1/merchants/$SUMUP_MERCHANT_CODE/readers/$SUMUP_READER_ID/checkout" \
--header "Authorization: Bearer $SUMUP_API_KEY" \
--header "Content-Type: application/json" \
--data @- <<JSON
{
"total_amount": {
"currency": "$SUMUP_CURRENCY",
"minor_unit": 2,
"value": $SUMUP_AMOUNT_VALUE
},
"description": "Quickstart payment",
"affiliate": {
"app_id": "$SUMUP_APP_ID",
"key": "$SUMUP_AFFILIATE_KEY",
"foreign_transaction_id": "$SUMUP_FOREIGN_TRANSACTION_ID"
}
}
JSON
)"
echo "$SUMUP_READER_CHECKOUT_RESPONSE" | jq
export SUMUP_READER_CHECKOUT_ID="$(echo "$SUMUP_READER_CHECKOUT_RESPONSE" | jq -r '.data.checkout_id')"

The accepted response contains identifiers for reconciliation:

{
"data": {
"checkout_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"client_transaction_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
}

If checkout_id is empty or null, stop and inspect the complete response.

The checkout should appear on Virtual Solo within 60 seconds.

  1. Confirm the amount and start the simulated payment.
  2. Follow the on-screen flow. No real card is needed; Virtual Solo simulates the reader interaction and auto-approves simulated PIN entry.
  3. Wait until the device returns to its idle screen.

Do not start another checkout on the same reader while this checkout is active.

Retrieve the reader checkout from your backend or terminal:

SUMUP_READER_VERIFICATION_RESPONSE="$(
curl --fail-with-body --silent --show-error \
"https://api.sumup.com/v0.1/merchants/$SUMUP_MERCHANT_CODE/readers/$SUMUP_READER_ID/checkout/$SUMUP_READER_CHECKOUT_ID" \
--header "Authorization: Bearer $SUMUP_API_KEY"
)"
echo "$SUMUP_READER_VERIFICATION_RESPONSE" | jq

After the successful simulation, the relevant fields look like this:

{
"data": {
"checkout_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"client_transaction_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "successful",
"payment_type": "card",
"total_amount": {
"currency": "EUR",
"minor_unit": 2,
"value": 1200
}
}
}
  • successful: record the payment exactly once.
  • pending: wait and retrieve the checkout again.
  • failed: keep the order unpaid and inspect payment_failure_reason.
  • cancelled: keep the order unpaid and start a new checkout only if the merchant wants to retry.

Treat a callback as a notification only. Get Reader Checkout is the authoritative result for the POS order state.

Set the deliberate failure amount and a new foreign transaction ID:

export SUMUP_AMOUNT_VALUE="1100"
export SUMUP_FOREIGN_TRANSACTION_ID="quickstart-failure-$(date +%s)"

Repeat steps 3–5. Virtual Solo should complete the simulated attempt, and Get Reader Checkout should return failed. Never reuse the successful foreign_transaction_id or checkout ID.

Symptom What to check
Pairing code is rejected Pair within five minutes and make sure the code came from the selected sandbox Virtual Solo session.
Reader stays processing Confirm pairing on Virtual Solo, then retrieve the reader again. Re-pair if the reader becomes expired.
Checkout is rejected Confirm the reader is paired and online, the currency matches the merchant account, and the complete affiliate object is present.
Nothing appears on the reader The checkout must start on the device within 60 seconds. Retrieve the checkout before creating another attempt.
Duplicate transaction error Generate a new foreign_transaction_id for the new attempt.
Callback and API disagree Keep the POS order pending until Get Reader Checkout returns a final status.

Before accepting real card-present payments:

  1. Pair a physical Solo with the live merchant and validate reader connectivity.
  2. Use separate production API and Affiliate Keys with the correct application ID.
  3. Store the reader ID, checkout ID, client transaction ID, foreign transaction ID, merchant code, amount, and final status.
  4. Add an HTTPS return_url and process callbacks idempotently, then verify every result with Get Reader Checkout.
  5. Handle reader-offline, active-checkout, timeout, cancellation, and duplicate-reference scenarios.
  6. Run a small live transaction and reconcile it in the SumUp Dashboard before launch.

Building a native mobile app instead? Choose the Android or iOS Reader SDK. Use Payment Switch only when your app must hand off to the installed SumUp app.