Amilon Processor and API
This reference describes the active asynchronous Amilon issuer and the deprecated synchronous implementation that can appear in historical data. It covers contract and denomination resolution, request and response mapping, queue states, duplicate risks, audit evidence, operational commands, and support boundaries.
Processor modes and scope
AmilonAsyncProcessor is the active queued issuer. The async issuer migration marks it as asynchronous and moves existing Amilon brands from the legacy issuer. AmilonProcessor is marked deprecated and is no longer expected to be returned to.
| Capability | Async issuer | Legacy sync issuer |
|---|---|---|
| Create call | POST /orders/create/{contractCode} | Same provider create operation through the legacy API wrapper |
| Voucher retrieval | GET orders/{processorReference}/complete/ | No separate complete retrieval; reads Vouchers[0] from create response |
| Processor reference | Response ExternalOrderId | Response ExternalOrderId |
| Completion rule | CardCode is present | Create response contains the voucher data expected by the sync implementation |
| Cancellation | Not implemented | Not implemented in the inspected legacy class |
| Balance | Contract-float lookup, not card balance | No customer-card balance operation in the inspected processors |
| Vault | uses_vault is false in the async issuer migration | Legacy issuer also does not use a vault |
Async lifecycle
- A sale supplies a partner, currency, brand, and face value. The API resolves a partner-specific contract when configured and otherwise uses the global contract for that currency.
- The API normalizes the face value and looks up the brand attribute
amilon-product-code-<normalized amount>. Product-code mappings are cached per brand for one hour. - The async create payload contains the sale UUID as
ExternalOrderIdand oneOrderRowsitem with the mappedProductIdandQuantity1. POST /orders/create/{contractCode}returns an Amilon payload. The response object mapsExternalOrderIdas the processor reference. If the first voucher has aCardCode, the application card is successful immediately; otherwise it is pending.- A pending create is requeued by
AsyncProcessRequest; when an existing-order exception suppliesCODE_ORDER_EXISTS, the job instead schedulesAsyncFetchCode. The retrieval job callsGET orders/{processorReference}/complete/and maps the returned voucher. The complete URL includes an empty client-auth segment and therefore ends with/complete/. - A voucher with a code is stored with its normalized amount, code, and
VoucherLink, and the sale becomes successful. A voucher without a code remains pending and is retried by the retrieval job.
An accepted order and a completed voucher are different provider states. The payload may contain OrderStatus, but the response mapper uses the presence of Vouchers[0].CardCode as the application completion test.
!WARNING Do not manually create a replacement order while the original
ExternalOrderIdis unresolved. A create request can be accepted even when the code is not yet available, and another create request can produce a duplicate provider order or an “order already exists” response.
Redacted request and response examples
The active create request is JSON. Identifiers and product codes below are synthetic.
{
"ExternalOrderId": "sale-uuid-example",
"OrderRows": [
{
"ProductId": "product-code-example",
"Quantity": 1
}
]
}
The tests use the following response shape. Voucher code and link values are redacted here:
{
"ExternalOrderId": "external-order-example",
"Vouchers": [
{
"Amount": "25.00",
"CardCode": "[REDACTED]",
"VoucherLink": "https://provider.example/voucher/redacted"
}
],
"OrderStatus": "Completed"
}
The authentication request is form encoded and includes client_id, client_secret, username, password, and grant_type=password. Do not copy credentials, voucher codes, PINs, customer details, or live voucher links into documentation or support tickets.
Response mapping and validation
AmilonGiftCard::translateApiResponse() reads only the first item in Vouchers and maps these fields:
| Amilon field | Application field | Rule |
|---|---|---|
ExternalOrderId | processorReference | Identifies the provider order and is retained for retrieval |
Vouchers[0].Amount | faceValue | Normalized and cast to a float |
Vouchers[0].CardCode | code | Presence makes the application card successful |
Vouchers[0].VoucherLink | thirdPartyUrl | Stored as the provider voucher URL |
The parser requires the Vouchers array and its first item. Missing structural fields raise AmilonException with CODE_REPEAT_REQUEST, and the async job can retry the request. A valid structure with a missing or empty CardCode produces a pending card instead. The parser does not use Pin, ValidityEndDate, OrderStatus, GrossAmount, or NetAmount when creating the async application card.
The HTTP client decodes JSON responses and rejects a response that cannot be represented as an array. Guzzle failures are wrapped by AmilonException; timeout messages receive a gateway-timeout code, the known retry messages receive CODE_REPEAT_REQUEST, and the exact provider “order already exists” message receives CODE_ORDER_EXISTS.
Provider states, retries, and duplicate risk
| Condition | Application handling | Investigation meaning |
|---|---|---|
Vouchers[0].CardCode present | Mark success and complete the sale | Voucher details are available |
Voucher structure valid but CardCode empty | Mark pending; retrieval can be queued again | Provider order exists, but code is not available in the payload |
Missing Vouchers or malformed payload | Raise repeatable AmilonException; audit the response | Check provider payload shape and retry history |
| Timeout or retryable provider message | Async job repeats the request | Determine whether the original create was accepted before retrying |
| Exact “order with Id … already exists!” provider error | Map to CODE_ORDER_EXISTS; AsyncProcessRequest schedules AsyncFetchCode | Use the original external order ID and retrieve it; do not create a replacement |
| Other create/retrieve exception | Restore the sale to a retryable state or fail the job according to queue handling | Check exception code, response body, and job history |
AmilonAsyncProcessor does not define a processor-local maximum retry count. AsyncProcessRequest repeats create errors with CODE_REPEAT_REQUEST; AsyncFetchCode repeats retrieval errors with the same code and requeues pending cards using retrieval delay metadata. Queue configuration and job history are therefore part of the retry investigation.
Audit and reconciliation fields
Compare safe identifiers and metadata across the sale, processor interaction log, and provider response:
| Local or audit field | Amilon value to compare |
|---|---|
| Sale UUID | Create ExternalOrderId |
| Partner and currency | Resolved partner/global contract and currency code |
| Contract | contractCode in POST /orders/create/{contractCode}; configuration calls this value a contract code |
| Face value | Normalized denomination used in the brand-attribute lookup and voucher Amount |
| Product mapping | OrderRows[0].ProductId and amilon-product-code-<amount> |
| Quantity | OrderRows[0].Quantity, normally 1 |
| Processor reference | Response ExternalOrderId, stored as processor_ref |
| Completion | Presence of Vouchers[0].CardCode and VoucherLink; compare presence or a redacted fingerprint, not raw values |
| Time and retry context | Sale timestamps, request/retrieval attempt history, and exception code |
The async processor stores its generated request, provider response, and exception response body in processor interaction audit records. Authentication requests and responses are also initialized against the sale UUID. The unit tests assert one request and one response record for successful create and retrieval paths, and assert that an invalid response body is retained for investigation.
Settings and brand mappings
config/amilon.php reads these settings:
| Setting | Environment variable | Purpose |
|---|---|---|
| SSO URL | AMILON_ENVIRONMENT_SSO_URL | Authentication base URL; token path appends /connect/token |
| Web API URL | AMILON_ENVIRONMENT_WEB_API_URL | API base used with /b2bwebapi/{AMILON_API_VERSION} |
| API version | AMILON_API_VERSION | Version segment for the web API base URI |
| Credentials | AMILON_CLIENT_ID, AMILON_CLIENT_SECRET, AMILON_USERNAME, AMILON_PASSWORD | OAuth password-grant authentication |
| Global contract codes | AMILON_CONTRACT_ID, AMILON_CONTRACT_ID_GBP, AMILON_CONTRACT_ID_CHF, AMILON_CONTRACT_ID_PLN | Contract per configured global currency |
| Partner contract overrides | AMILON_CONTRACT_ID_EDENRED_ITALY, AMILON_CONTRACT_ID_DIGGECARD_EDENRED_ITALY | EUR contract overrides for configured partner slugs |
| Float monitoring | AMILON_AVAILABLE_FLOAT_ALERT_ENABLED and AMILON_AVAILABLE_FLOAT_ALERT_LIMIT_* | Enable alerts and set per-currency limits |
| Catalogue culture | AMILON_BRAND_INFO_CULTURE_IT_IT, AMILON_BRAND_INFO_CULTURE_EN_UK | Culture passed to product and catalogue calls |
| Notification recipients | FINANCE_EMAIL_ADDRESS, TECHTEAM_EMAIL_ADDRESS | Command notification configuration |
AMILON_API_URL is present as a legacy endpoint configuration value and is annotated as unused in config/amilon.php. The active API builds its base URI from AMILON_ENVIRONMENT_WEB_API_URL and AMILON_API_VERSION.
Each enabled brand needs amilon-product-code-<normalized amount> for every sellable fixed denomination. The stored attribute value is sent as ProductId. A missing attribute raises a missing-denomination-code exception before the provider order is created. The one-hour product-code cache means a mapping change may require cache expiry or invalidation before it is observed.
API operations and operational tools
| Operation | Endpoint or command | Use |
|---|---|---|
| Authenticate | POST {SSO_URL}/connect/token | Form-encoded password grant; token is cached with a 30-second expiry reduction |
| Create order | POST /b2bwebapi/{version}/orders/create/{contractCode} | Create a voucher order with ExternalOrderId and OrderRows |
| Complete order | GET /b2bwebapi/{version}/orders/{processorReference}/complete/ | Retrieve voucher details for an existing order |
| Available products | GET /b2bwebapi/{version}/contracts/{contractId}/{culture}/products | Read active contract products and prices |
| Complete catalogue | GET /b2bwebapi/{version}/contracts/{contractId}/{culture}/products/complete | Retrieve detailed product and retailer data |
| Contract information | GET /b2bwebapi/{version}/contracts/{contractId} | Read contract balance data for float checks |
| Float check | php artisan rewardcloud:check-amilon-available-float | Email alerts when configured contract float is below its limit; supports --no-send and --override-recipient-email |
| Denomination availability | php artisan rewardcloud:check-amilon-brand-denominations-availability | Update brand-denomination availability from global contract product prices |
| Brand information export | php artisan report:amilon-brands-information-export | Export retailer/products information; supports --no-send and --override-recipient-email |
The denomination availability command checks global currency contracts and applies the resulting product prices to matching brands. Partner-specific contracts use the same product codes as the corresponding global contract according to the service comment, so a change still needs to be checked against the affected partner catalogue.
Investigation checklist
- Record the sale UUID, partner slug, brand slug, currency, normalized face value, sale status, and
processor_ref. - Resolve the contract mapping: check the partner override first, then the global currency contract, and confirm that the configured value is a contract code.
- Check
amilon-product-code-<normalized amount>and confirm its cached value matches the current provider product code. - Compare the audited request’s
ExternalOrderId,ProductId, and quantity with the sale and brand mapping. - Check the create response for
ExternalOrderId,Vouchers,Amount,CardCode,VoucherLink, andOrderStatus; treatCardCodepresence, notOrderStatusalone, as completion evidence. - If retrieval is pending, call history and job records should show
orders/{processorReference}/complete/attempts and retrieval delay metadata. - If a duplicate error appears, stop replacement orders and search by the original external order ID before scheduling retrieval.
- Check authentication audit entries without exposing credentials, then compare provider status, exception code, timestamps, and response body shape.
- For float or catalogue issues, run the relevant command in the correct environment and compare the contract, culture, product code, price, and brand-denomination status.
Limitations and support
The async processor does not implement customer-card cancellation or card-balance lookup. retrieveContractBalance() and the available-float command report processor contract float, not the remaining value of an issued voucher. The async issuer migration sets uses_vault to false.
Escalations should include the sale UUID, external order ID, contract context, product mapping, timestamps, safe audit comparisons, and exception codes. Do not include credentials, raw CardCode, PIN values, customer details, or live VoucherLink values unless the approved support process explicitly requires protected data.
Related code
app/Processors/Amilon/AmilonAsyncProcessor.php— async create, retrieval, status mapping, and audit interaction handling.app/Processors/Amilon/AmilonApi.php— authentication, contract selection, product-code lookup, order, retrieval, catalogue, and float calls.app/Processors/Amilon/ResponseObjects/AmilonGiftCard.php— response validation and field mapping.app/Processors/Amilon/Exception/AmilonException.php— retry, timeout, duplicate-order, and malformed-response classifications.app/Processors/AmilonProcessor.php— deprecated synchronous create-response behavior.config/amilon.php— URLs, credentials, contract codes, cultures, and alert configuration.app/Jobs/AsyncProcessRequest.phpandapp/Jobs/AsyncFetchCode.php— pending and retry queue transitions.app/Console/Commands/CheckAmilonAvailableFloat.php— contract-float alert command.app/Console/Commands/CheckAmilonBrandDenominationsAvailability.php— product availability and denomination status command.app/Console/Commands/GenerateAmilonBrandsInformationExport.php— catalogue export command.tests/Unit/Processors/Amilon/AmilonAsyncProcessorTest.php— async mapping, pending/error, and audit scenarios.tests/Unit/Processors/Api/Amilon/AmilonApiTest.php— request paths, payload shape, contracts, catalogue, and response mapping examples.database/migrations/2021_09_30_161102_rcc_8962_add_amilon_async_processor.php— async issuer, support contact, and wiki metadata.database/migrations/2021_11_01_133415_r_c_c-9194-move-amilon-brands-over-from-sy.php— migration of legacy Amilon brands to async.