The Cheqi sandbox is a fully functional test environment where you can build and test your integration without affecting production data. It mirrors the production API with pre-configured test customers so you can verify the complete receipt flow end-to-end.
| API Base URL | https://sandbox.api.cheqi.io |
| Merchant Portal | sandbox.portal.cheqi.io |
| Status | Fully functional, mirrors production API |
The sandbox environment is functionally identical to production. The only differences are:
- Client applications are auto-approved (no review wait time)
- Pre-configured test customers with known card PARs and emails
- A merchant portal for managing companies, keys, and viewing receipts
Go to sandbox.portal.cheqi.io and register with your email address. You'll receive a verification code to complete sign-up.
Once logged in, create a company from the Dashboard (or click Add Company). This registers your merchant identity in the sandbox.
From the Dashboard, generate an API Key for direct API access.
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.
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.
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 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.pemThis 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.
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.
- Go to the Dashboard on sandbox.portal.cheqi.io
- Find your client application
- Paste the contents of
public_key.pem, or select thepublic_key.pemfile - 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.
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.
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:
| Algorithm | Description |
|---|---|
RSA_2048 | RSA 2048-bit with RSA-OAEP (SHA-256) |
RSA_4096 | RSA 4096-bit with RSA-OAEP (SHA-256) |
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);For manual integration, follow these steps:
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" } }Build the definitive receipt payload locally. Do not send its plaintext contents to Cheqi.
Encrypt the same payload independently for every returned owner device (AES-256-GCM + RSA-OAEP, with a fresh AES key per device).
Submit the complete per-device encrypted set:
POST https://sandbox.api.cheqi.io/receipt/encrypted
Go to the Receipts tab in the merchant portal to see your submitted receipt. Click on it to view delivery status and, when available, the decrypted content. To see decrypted content in the portal, issue the receipt against one of the sandbox test customer identifiers shown in the Test Customers section of the portal. This proves the full end-to-end flow works: customer matching, per-device encryption, on-device generation, queuing, and decryption.
The sandbox merchant portal can show decrypted receipt content only for receipts delivered to Cheqi's pre-configured sandbox test customers. Use one of the test customer identifiers shown in the portal, such as a test PAR or email address, when resolving the recipient for the receipt.
This is a sandbox-only testing feature. The sandbox stores a private key for each seeded test customer, so when a receipt is delivered to one of those test-customer devices, the portal can decrypt that device copy and show:
- the decrypted generation input and generated receipt
- recipient delivery status for the issued receipt
The portal does not decrypt arbitrary receipts. A receipt may have multiple encrypted copies, for example one for a customer device, one for a company, and one for a client application. The sandbox preview specifically looks for the copy encrypted to a sandbox test customer because that is the only customer-device private key Cheqi has in the sandbox.
Sandbox test customers are shared fixtures across the sandbox environment; they are not created separately for each merchant company. The receipt preview is still scoped to your company: the portal only lists and decrypts receipts that your company issued. Another sandbox company cannot open your receipts just because it can see the same test customer identifiers.
This decrypted preview does not exist in production. In production, Cheqi does not store customer private keys and cannot read end-user receipt contents. Receipt payloads are decrypted by the receiving app, device, or client application that owns the matching private key.
If the portal shows that decrypted preview is unavailable, the receipt was not delivered to a sandbox test customer. Use one of the PARs or emails from the Test Customers section when you want to verify decrypted content in the portal.
The sandbox has pre-configured test customers with known card PARs and emails. These test customers are shared across sandbox merchants. Go to the Test Customers section in the merchant portal to see the available identifiers you can use when resolving recipients. Receipts issued against these identifiers can be opened in the portal with decrypted receipt content, 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));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:
IdentificationDetails customer = new IdentificationDetails()
.paymentType(PaymentType.CARD_PAYMENT)
.pairingCode("84739201"); // 8-digit code from customer's appPairing codes are valid for 5 minutes and can only be used once. See Recipient Resolution for full details.
Point your SDK to the sandbox API:
CheqiSDK sdk = CheqiSDK.builder()
.apiEndpoint(Environment.SANDBOX)
.apiKey(System.getenv("CHEQI_SANDBOX_API_KEY"))
.build();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.
- Receipt Flow Overview - Understand the complete receipt delivery pipeline
- Authentication - API Keys vs OAuth 2.0
- Java SDK - Full Java SDK documentation
- JavaScript SDK - Full JavaScript and TypeScript SDK documentation
- Cheqi CLI - Command-line receipt workflows
- SDK & CLI Availability - Review supported integration tooling