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.
You are finished when Virtual Solo completes the simulated payment and Get Reader Checkout returns successful.
What runs where
Section titled “What runs where”| 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. |
Prerequisites
Section titled “Prerequisites”- 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
curlandjq. - A browser in which to run Virtual Solo.
Sandbox merchant account
Section titled “Sandbox merchant account”- Log in to the SumUp Dashboard(Opens in a new tab).
- Open Developer Settings(Opens in a new tab).
- Create a sandbox merchant account, then select it in the Dashboard account switcher.
- With the sandbox merchant selected, go to Settings > For Developers > Toolkit.
- Under API Keys, create and copy an API key. Do not use the SumUp Public Key.
- Under Affiliate Keys, create a key for an application ID you control, such as
com.example.quickstart. - 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.
1. Configure your terminal
Section titled “1. Configure your terminal”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"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.
2. Pair Virtual Solo
Section titled “2. Pair Virtual Solo”- Open Virtual Solo(Opens in a new tab) and select the sandbox environment.
- In the simulated reader, open Connections > API > Connect.
- Copy the displayed pairing code. It expires after five minutes.
- 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" | jqexport 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" | jqContinue 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.
3. Start the reader checkout
Section titled “3. Start the reader checkout”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" | jqexport 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.
4. Complete the payment on Virtual Solo
Section titled “4. Complete the payment on Virtual Solo”The checkout should appear on Virtual Solo within 60 seconds.
- Confirm the amount and start the simulated payment.
- Follow the on-screen flow. No real card is needed; Virtual Solo simulates the reader interaction and auto-approves simulated PIN entry.
- Wait until the device returns to its idle screen.
Do not start another checkout on the same reader while this checkout is active.
5. Verify the result
Section titled “5. Verify the result”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" | jqAfter 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 inspectpayment_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.
6. Test a failed payment
Section titled “6. Test a failed payment”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.
Troubleshooting
Section titled “Troubleshooting”| 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. |
Move to production
Section titled “Move to production”Before accepting real card-present payments:
- Pair a physical Solo with the live merchant and validate reader connectivity.
- Use separate production API and Affiliate Keys with the correct application ID.
- Store the reader ID, checkout ID, client transaction ID, foreign transaction ID, merchant code, amount, and final status.
- Add an HTTPS
return_urland process callbacks idempotently, then verify every result with Get Reader Checkout. - Handle reader-offline, active-checkout, timeout, cancellation, and duplicate-reference scenarios.
- 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.