APIGiftCardController

Technical reference for the Scanfie Gift Card REST API

Base path: /api/giftcard apps.resapp.controllers.APIGiftCardController extends AbstractAPIController

Overview

The APIGiftCardController exposes a JSON REST API that allows external systems to query, redeem and roll back gift cards. It is intended for integrations such as a POS system or an external payment terminal that needs to process Scanfie gift cards.

All three endpoints follow the same flow:

  1. Authenticate the caller using an API key and secret
  2. Validate the request body (required fields, amounts, CVC)
  3. Execute the mutation on the gift card wallet
  4. Return a JSON response (always HTTP 200 — errors are returned inside the body)

Authentication

Every request must include two HTTP headers:

HeaderDescription
X-API-keyThe API key identifier
X-API-secretThe corresponding secret

The AbstractAPIController.authenticateRequest() method validates the key pair via APIKey.calculateAuthenticatedKey(key, secret). If the key is valid, the associated Company is set as the contextual company for the remainder of the request. If the key is invalid, an error is returned immediately without further processing.

Response Envelope

All responses are wrapped in an APICallResponseWrapper. The HTTP status code is always 200 OK — the caller must inspect the errors list to determine whether the call succeeded.

// Success example
{
  "valid": true,
  "response": {
    "errors": [],
    // ...fields specific to each endpoint...
  }
}

// Failure example
{
  "valid": false,
  "response": {
    "errors": [
      { "field": "json", "message": "Giftcard not found" }
    ]
  }
}

Endpoints

POST /api/giftcard/query Read-only

Retrieves the balance and validity of a gift card based on its external ID and CVC code. This endpoint does not mutate anything — it is purely informational. Use the returned internalId in subsequent /redeem calls.

Request body

{
  "giftCardId": "SCAN-12345",
  "giftCardCVC": "A1B2"
}
FieldTypeRequiredDescription
giftCardIdString Required The external ID of the gift card (CompanyGiftCard.externalId)
giftCardCVCString Required The CVC security code of the card

Validation steps (in order)

  1. giftCardId must not be empty
  2. An active company context must exist
  3. The gift card must exist (CompanyGiftCard.getByExternalId)
  4. giftCardCVC must not be empty
  5. CVC must match giftCard.calculateCVCCode()
  6. The gift card must not be expired (giftCard.isExpired())

Response fields

{
  "internalId": "550e8400-e29b-41d4-a716-446655440000",
  "balance": 25.00,
  "redeemable": true,
  "errors": []
}
FieldTypeDescription
internalIdStringInternal UUID of the gift card — pass this to /redeem
balanceDoubleCurrently available balance in euros
redeemableBooleanfalse if there are errors or no wallet is linked
POST /api/giftcard/redeem Mutating
⚠️
This endpoint mutates the wallet. A DirectPayment record is created and the wallet balance is reduced. Store the returned associatedTransactionId — it is required to perform a rollback.

Deducts an amount from the gift card wallet via Wallet.reductionViaPayment().

Request body

{
  "giftCardInternalId": "550e8400-e29b-41d4-a716-446655440000",
  "giftCardCVC": "A1B2",
  "amountToRedeem": 15.00
}
FieldTypeRequiredDescription
giftCardInternalIdString Required Internal UUID of the gift card (obtained via /query)
giftCardCVCString Required CVC security code of the card
amountToRedeemDouble Required Amount to deduct in euros — must be > 0

Validation steps (in order)

  1. giftCardInternalId must not be empty
  2. amountToRedeem must not be null or ≤ 0
  3. An active company context must exist
  4. The gift card must exist (CompanyGiftCard.getByInternalId)
  5. giftCardInternalId must match the ID of the found card
  6. CVC must match giftCard.calculateCVCCode()
  7. The gift card must not be expired
  8. Sufficient balance must be available (Wallet.canDeductAmount)
  9. A WalletPaymentMethod of type GIFTCARD must exist for the company

Processing

A new DirectPayment object is created and persisted with the following values:

FieldValue
statusSUCCESSFUL
referenceIdgiftCard.id
amountamountToRedeem
selectedPaymentMethodwalletPaymentMethod (type GIFTCARD)
dtPaidnow()
giftCardthe resolved CompanyGiftCard
turnoverRegistrationGroupdefault gift card reservation group

The wallet is then reduced via Wallet.reductionViaPayment(payment).

Response fields

{
  "associatedTransactionId": "7f3e9a12-0c1d-4e2b-b8f0-123456789abc",
  "redeemedAmount": 15.00,
  "balanceAfterRedeem": 10.00,
  "errors": []
}
FieldTypeDescription
associatedTransactionIdStringUUID of the created DirectPayment — store this for rollback
redeemedAmountDoubleAmount actually redeemed
balanceAfterRedeemDoubleBalance after redemption
POST /api/giftcard/rollback Mutating
⚠️
A rollback is only possible if the transaction has status SUCCESSFUL and has not already been rolled back or cancelled.

Reverses a previously executed redemption. The DirectPayment is set to REFUNDED and the balance is restored via Wallet.rollbackViaPayment().

Request body

{
  "transactionId": "7f3e9a12-0c1d-4e2b-b8f0-123456789abc",
  "giftCardInternalId": "550e8400-e29b-41d4-a716-446655440000"
}
FieldTypeRequiredDescription
transactionIdString Required The associatedTransactionId from the /redeem response
giftCardInternalIdString Required Internal UUID of the gift card

Validation steps (in order)

  1. transactionId must not be empty
  2. giftCardInternalId must not be empty
  3. An active company context must exist
  4. The DirectPayment must exist (BaseDao.getById)
  5. The gift card must exist (CompanyGiftCard.getByInternalId)
  6. DirectPayment.giftCard must match the provided gift card
  7. The transaction must not already be REFUNDED
  8. The transaction must not already be CANCELLED
  9. The transaction must have status PAID (= SUCCESSFUL)

Processing

The DirectPayment is updated with the following values:

FieldValue
statusREFUNDED
dtRefundednow()
dtCancellednull

The wallet balance is then restored via Wallet.rollbackViaPayment(payment).

Response fields

{
  "associatedTransactionId": "7f3e9a12-0c1d-4e2b-b8f0-123456789abc",
  "rollbackAmount": 15.00,
  "balanceAfterRollback": 25.00,
  "errors": []
}
FieldTypeDescription
associatedTransactionIdStringUUID of the rolled-back DirectPayment
rollbackAmountDoubleAmount reversed (= payment.amount)
balanceAfterRollbackDoubleBalance after rollback

Recommended Integration Flow

1. POST /api/giftcard/query
    ← receive: internalId, balance, redeemable

2. (optional: show balance to customer / POS)

3. POST /api/giftcard/redeem (using internalId from step 1)
    ← receive: associatedTransactionId, redeemedAmount, balanceAfterRedeem

4. (on payment failure or order cancellation)
    POST /api/giftcard/rollback (using associatedTransactionId from step 3)

Error Codes

All errors are returned as an array in response.errors, each with a field and a message. The HTTP status is always 200.

Error messageEndpointCause
Giftcard id is requiredquerygiftCardId is empty
Invalid CVC codequery, redeemCVC is missing or does not match
Giftcard is expiredquery, redeemgiftCard.isExpired() == true
Giftcard not foundquery, redeem, rollbackNo card found with the provided ID
No active companyallNo active company context (authentication failure)
Invalid amount to redeemredeemamountToRedeem is null or ≤ 0
Insufficient fundsredeemBalance < requested amount
No wallet payment method found for accountredeemNo WalletPaymentMethod of type GIFTCARD configured
Transaction not foundrollbackDirectPayment not found
Transaction does not match giftcardrollbackpayment.giftCard ≠ provided gift card
Transaction already rolled backrollbackStatus is already REFUNDED
Transaction already cancelledrollbackStatus is already CANCELLED
Transaction is not successfulrollbackStatus is not PAID
Request could not be authenticatedallInvalid or missing API key / secret
Invalid request: <message>allMalformed JSON or IO error reading the request body

Requirements

Scanfie configuration

RequirementDescription
APIKey recordAn active API key linked to the company
CompanyGiftCard recordGift card with an externalId and a linked wallet
Wallet of type GIFTCARDMust be linked to the CompanyGiftCard
WalletPaymentMethod type GIFTCARDA wallet/gift card payment method must be configured for the company
TurnoverRegistrationGroupThe default gift card reservation payment registration group must exist

Technical dependencies

ClassRole
CompanyGiftCardLook up and validate the card; CVC calculation via calculateCVCCode()
WalletBalance mutations via reductionViaPayment() and rollbackViaPayment()
DirectPaymentTransaction record for audit trail and rollback capability
WalletPaymentMethodDetermines which payment method the redemption is booked under
TurnoverRegistrationGroupRevenue accounting for gift card redemptions
BaseDaoRetrieve DirectPayment by UUID during rollback