Skip to content

Sandbox Environment

The Cheqi sandbox is a test environment where you can build and test your integration without affecting production data. It uses the production receipt submission flow and provides pre-configured test customers for checking matching, encryption, and submission.

Availability

Daily sandbox downtime

The sandbox API and integration service are offline every day from 20:00 to 08:00 in Europe/Amsterdam (CET in winter, CEST in summer), including weekends. Sandbox API requests and integrations are unavailable during these hours, including actions in the sandbox merchant portal that require the API.

Services begin starting at 08:00. Allow a few minutes for startup before testing.

Sandbox Details

API Base URLhttps://sandbox.api.cheqi.io
Merchant Portalsandbox.portal.cheqi.io
StatusTest API with seeded customer fixtures
Sandbox vs Production

The sandbox uses the same receipt submission API as production, with these test-specific features:

  • Client applications are auto-approved (no review wait time)
  • Pre-configured test customers with known card PARs and emails
  • A merchant portal that can show submitted generation input for those fake test customers

Issue your first sandbox receipt

This path tests issuance without setting up a receiving integration or a public key. You only need a sandbox company and an API key with receipt:issue.

  1. Create an account at sandbox.portal.cheqi.io and register the company that will issue the receipt.

  2. Create a company API key with the Issue Receipts permission. Copy it when shown; the portal displays it only once.

  3. Install the Cheqi CLI, then run these commands with your key. SANDBOX-PAR-001 is a seeded test customer identifier; the portal's Test Customers section lists the available identifiers.

    npm install --global @cheqi/cli@0.4.0
    cheqi session create \
      --session sandbox-first-receipt \
      --document-number TEST-001 \
      --card-par SANDBOX-PAR-001
    cheqi receipt add-product \
      --session sandbox-first-receipt \
      --name "Coffee" \
      --price-incl 12.10 \
      --vat 21 \
      --sku TEST-COFFEE-001
    cheqi receipt validate --session sandbox-first-receipt
    CHEQI_API_KEY=sk_your_sandbox_api_key cheqi receipt finalize \
      --session sandbox-first-receipt
  4. In the merchant portal, open Receipts and select the new receipt. It can remain Awaiting device because the seeded fake customer does not run the production device processor. The sandbox portal can show the decrypted generation input you submitted; it does not show a finalized receipt.

For a receipt that a customer can open without the app, enable download fallback for your sandbox company and follow Web Wallet. The receipt link opens in the browser wallet or Cheqi app.

The sections below cover SDK and direct API integrations, receiving keys, and other sandbox scenarios.

Detailed setup and test options

1. Create an Account

Go to sandbox.portal.cheqi.io and register with your email address. You'll receive a verification code to complete sign-up.

2. Register a Company

Once logged in, create a company from the Dashboard (or click Add Company). This registers your merchant identity in the sandbox.

3. Get Your API Credentials

From the Dashboard, generate an API Key for direct API access. Select the scopes your integration needs; see API key scopes.

Include the key as a Bearer token in the Authorization header for all API calls:

curl https://sandbox.api.cheqi.io/recipient/resolve \
  -H "Authorization: Bearer sk_your_sandbox_api_key" \
  -H "Content-Type: application/json" \
  -d '{...}'

Alternatively, create a Client Application for OAuth2 integrations. Client applications are auto-approved in the sandbox, so you can start testing immediately.

4. Generate and Upload a Public Key

Cheqi uses hybrid encryption (AES-256-GCM + RSA-OAEP with SHA-256). Upload a public key when you need to receive encrypted content from Cheqi. Keep the matching private key in your own infrastructure and use it to decrypt the wrapped AES key in each envelope — Cheqi never needs your private key.

Skip this step when you only want to issue a receipt to a customer. It is not needed for the first-receipt path above.

You need a registered public key when you want to:

  • Receive encrypted receipt copies — fetch issued receipts through the API or process receipt webhooks for your own company or client application.
  • Receive and process customer returns — return requests are encrypted to your public key (your issuer key) and you decrypt them with the matching private key. Without a registered key, customers cannot send you returns and you cannot decrypt them.

If you only submit receipts for delivery to customer apps and do not handle returns, a public key is optional. Without one, Cheqi simply skips your company or client application as an encrypted downstream recipient; customer-app delivery still works.

Generate an RSA Key Pair

# Generate a 2048-bit RSA private key
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private_key.pem

# Extract the public key
openssl rsa -in private_key.pem -pubout -out public_key.pem

This creates:

  • private_key.pem: your PKCS#8 RSA private key (-----BEGIN PRIVATE KEY-----)
  • public_key.pem: the X.509 SubjectPublicKeyInfo public key to upload to Cheqi (-----BEGIN PUBLIC KEY-----)

Upload only public_key.pem. Never upload private_key.pem.

Keep Your Private Key Safe

Store private_key.pem securely — you'll need it to decrypt receipt copies sent to your application. Never share it or commit it to version control.

Upload via the Merchant Portal

  1. Go to the Dashboard on sandbox.portal.cheqi.io
  2. Find your client application
  3. Paste the contents of public_key.pem, or select the public_key.pem file
  4. Click Upload Key

The portal reads the public key PEM content, validates that it is a public key, and encodes it before sending it to the API. Uploading public_key.pem is equivalent to pasting the file contents. The portal rejects private-key files.

Upload via API

You can also upload the public key programmatically. The API expects the PEM content to be Base64-encoded (to avoid newline issues in JSON):

# Base64-encode the PEM file. This works on macOS and Linux.
PUBLIC_KEY_B64=$(openssl base64 -A -in public_key.pem)

# Upload to your client application
curl -X POST https://sandbox.api.cheqi.io/client-application/{clientApplicationId}/public-key \
  -H "Authorization: Bearer YOUR_COMPANY_API_KEY_OR_ADMIN_USER_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"publicKey\": \"$PUBLIC_KEY_B64\",
    \"keyAlgorithm\": \"RSA_2048\"
  }"

The backend decodes the Base64-encoded PEM, strips the PEM headers and whitespace, validates the key, and stores the normalized public key material. If you later retrieve the key through the API, the returned publicKey is the normalized key material, not the original PEM file.

Company keys (API-key integrations)

If you integrate with a company API key rather than a client application, upload the company's public key instead. This is the key used as your issuer key when you receive customer returns issued under your company:

PUBLIC_KEY_B64=$(openssl base64 -A -in public_key.pem)

curl -X POST https://sandbox.api.cheqi.io/company/public-key \
  -H "Authorization: Bearer YOUR_COMPANY_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"publicKey\": \"$PUBLIC_KEY_B64\",
    \"keyAlgorithm\": \"RSA_2048\"
  }"

Supported key algorithms:

AlgorithmDescription
RSA_2048RSA 2048-bit with RSA-OAEP (SHA-256)
RSA_4096RSA 4096-bit with RSA-OAEP (SHA-256)

5. Submit a Receipt

Choose your integration method:

CheqiSDK sdk = CheqiSDK.builder()
    .apiEndpoint(Environment.SANDBOX)
    .apiKey("sk_your_sandbox_api_key")
    .build();

IdentificationDetails id = new IdentificationDetails()
    .paymentType(PaymentType.CARD_PAYMENT)
    .cardDetails(new CardDetails()
        .paymentAccountReference("SANDBOX-PAR-001")
        .cardProvider(CardDetails.CardProviderEnum.VISA));

ReceiptPayload receipt = ReceiptPayload.builder()
    .documentNumber("INV-001")
    .issueDate(OffsetDateTime.now())
    .currency("EUR")
    .totalAmount(new BigDecimal("12.10"))
    .totalTaxAmount(new BigDecimal("2.10"))
    .totalBeforeTax(new BigDecimal("10.00"))
    .receiptSubtotal(new BigDecimal("10.00"))
    .products(List.of(
        Product.builder()
            .name("Cappuccino")
            .identifier("SKU-CAPPUCCINO-001")
            .quantity(1.0)
            .baseQuantity(1.0)
            .unitCode(UnitCode.C62)
            .unitPrice(new BigDecimal("10.00"))
            .subtotal(new BigDecimal("10.00"))
            .total(new BigDecimal("12.10"))
            .addTax(21.0, "VAT", "10.00", "2.10")
            .build()
    ))
    .addTax(Tax.builder()
        .rate(21.0)
        .type("VAT")
        .taxableAmount("10.00")
        .amount("2.10")
        .label("VAT 21%")
        .build())
    .taxesApplied(true)
    .build();

ReceiptResult result = sdk.getReceiptService()
    .issueReceipt(id, receipt);

These examples test the PAR-based DIGITAL route. To test the web wallet link route when a terminal has no PAR, provide paymentType without a card identifier in IdentificationDetails and put the provider's four card digits in the encrypted ReceiptPayload.paymentDetails.cardLastFour. Enable download fallback for the sandbox company or client application. See the Delivery Options and Web Wallet guide.

REST API

For manual integration, follow these steps:

  1. Resolve recipient:

    POST https://sandbox.api.cheqi.io/recipient/resolve
    Authorization: Bearer sk_your_api_key
    Content-Type: application/json
    
    {
      "cardDetails": {
        "paymentAccountReference": "SANDBOX-PAR-001",
        "cardProvider": "VISA"
      }
    }
  2. Build the definitive receipt payload locally. Do not send its plaintext contents to Cheqi.

  3. Encrypt the payload for all returned recipients using the SDK.

  4. Submit the complete per-device encrypted set:

    POST https://sandbox.api.cheqi.io/receipt/encrypted

6. Verify Delivery

Go to the Receipts tab in the merchant portal to see your submitted receipt. Click on it to view its processing status and, when available, the decrypted generation input. To inspect that input, issue the receipt against an identifier in the portal's Test Customers section. This verifies customer matching, encrypted submission, and the submitted input. It does not confirm completed delivery to a real customer.

Sandbox generation input preview

The sandbox merchant portal can show the decrypted generation input only for receipts addressed to Cheqi's pre-configured fake test customers. Use a test PAR or email from Test Customers when resolving the recipient.

Use fake receipt data with seeded test customers. Cheqi controls these test customers' keys, so authorized administrators of the issuing company can inspect the submitted input. Shared test identifiers do not give other merchants access to your submissions.

The preview shows submitted input, not a finalized receipt or proof of customer delivery. The receipt can remain Awaiting device. Production has no seeded test-customer keys or input preview; merchants see submission and delivery status.

If the portal says the generation input is unavailable, check that you used one of the PARs or emails from Test Customers and selected the issuing company.

Test Customers

The sandbox has pre-configured fake test customers with known card PARs and emails. These fixtures are shared across sandbox merchants. Go to Test Customers in the merchant portal to see their identifiers. Receipts issued against them can be opened with decrypted generation input, but only by the company that issued the receipt.

Use these test PARs in your IdentificationDetails to simulate real customer matching:

IdentificationDetails customer = new IdentificationDetails()
    .paymentType(PaymentType.CARD_PAYMENT)
    .cardDetails(new CardDetails()
        .paymentAccountReference("SANDBOX-PAR-001")  // Test customer PAR
        .cardProvider(CardDetails.CardProviderEnum.VISA));

Pairing Codes

In production, customers can generate a temporary pairing code in the Cheqi app to identify themselves at checkout. This is especially useful for cash payments, gift cards, or any scenario where card data isn't available.

The customer shows an 8-digit code (or QR code) to the merchant, who includes it when submitting the receipt:

This is the customer-to-merchant pairing flow. With a web wallet link, the merchant shares a receipt URL with the customer instead and does not wait for a pairing code.

IdentificationDetails customer = new IdentificationDetails()
    .paymentType(PaymentType.CARD_PAYMENT)
    .pairingCode("84739201");  // 8-digit code from customer's app

Pairing codes are valid for 5 minutes and can only be used once. See Recipient Resolution for full details.

SDK Configuration for Sandbox

Point your SDK to the sandbox API:

CheqiSDK sdk = CheqiSDK.builder()
    .apiEndpoint(Environment.SANDBOX)
    .apiKey(System.getenv("CHEQI_SANDBOX_API_KEY"))
    .build();

Company Management

You can register multiple companies to test different merchant setups. Use the company switcher in the navigation bar to switch between them. Companies can be deactivated from the Dashboard when no longer needed — all API keys will be revoked but receipt data is retained for compliance.

Next Steps