1. Go to this page and download the library: Download snipershady/amilon library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
snipershady / amilon example snippets
use Amilon\Dto\CredentialDto;
use Amilon\Dto\Request\CreateOrderRequestDto;
use Amilon\Enum\CountryEnum;
use Amilon\Enum\Environment;
use Amilon\Service\AmilonClientFactory;
$client = AmilonClientFactory::create(new CredentialDto(
username: $secrets->get('amilon_username'),
password: $secrets->get('amilon_password'),
clientId: $secrets->get('amilon_client_id'),
clientSecret: $secrets->get('amilon_client_secret'),
authDomain: 'https://b2bstg-sso.amilon.eu/',
webDomain: 'https://b2bstg-webapi.amilon.eu/b2bwebapi/v1/',
webDomainV2: 'https://b2bstg-webapi.amilon.eu/b2bwebapi/v2/',
contractId: '1ab2c3d4-567e-4b0c-b8da-a3ed94ae6392',
environment: Environment::STAGING,
));
// browse the catalogue: one entry per merchant, each with a list of denominations
$merchants = $client->getDenominations(CountryEnum::IT);
foreach ($merchants as $merchant) {
foreach ($merchant->denominations as $denomination) {
// denomination shape: ->isFixed() / ->isVariable() / ->hasContractPriceOverride()
$price = $denomination->prices[0]->price ?? $denomination->rangeMin;
echo $merchant->code, ' ', $merchant->name, ' ', $price, PHP_EOL;
}
}
// place an order: identify the item by merchant code + chosen face value.
// pick the first denomination that can actually be ordered — one with an
// explicit price or an open range (skip a denomination that has neither)
$merchant = $merchants->all()[0];
$denomination = $merchant->denominations[0];
foreach ($merchants as $candidate) {
foreach ($candidate->denominations as $option) {
if ([] !== $option->prices || $option->isVariable()) {
[$merchant, $denomination] = [$candidate, $option];
break 2;
}
}
}
$price = $denomination->prices[0]->price ?? $denomination->rangeMin;
$order = $client->makeOrder(
CreateOrderRequestDto::singleLineWithPrice('my-order-001', $merchant->code, 1, $price),
);
foreach ($order->vouchers as $voucher) {
echo $voucher->voucherLink, PHP_EOL;
}
// read it back later — summary only, or ...Complete() for the vouchers too
$order = $client->getOrderInfoComplete('my-order-001');
// check the spendable balance
echo $client->getContractInfo()->currentAmount, PHP_EOL;
use Amilon\Dto\Request\CreateOrderRequestDto;
use Amilon\Dto\Request\OrderLineDto;
// one merchant, one face value
$order = $client->makeOrder(
CreateOrderRequestDto::singleLineWithPrice('my-order-001', $merchantCode, 1, 20.0),
);
// or several merchants in a single order
$order = $client->makeOrder(CreateOrderRequestDto::fromLines('my-order-002', [
OrderLineDto::withPrice($merchantCodeA, 2, 25.0),
OrderLineDto::withPrice($merchantCodeB, 1, 50.0),
]));
foreach ($order->vouchers as $voucher) {
echo $voucher->voucherLink, PHP_EOL;
}
$order = $client->makeOrderPostponed(
CreateOrderRequestDto::singleLineWithPrice('my-order-003', $merchantCode, 1, 20.0),
new DateTimeImmutable('+7 days'),
);
$order->orderStatus; // e.g. "Pending" (->status() is null — not a modelled state)
$order->vouchers; // [] — issued later
$order = $client->getOrderInfoComplete('my-order-003');
if ($order->status()?->isCompleted()) {
foreach ($order->vouchers as $voucher) {
echo $voucher->voucherLink, PHP_EOL;
}
}
$info = $client->getContractInfo();
if ($info->currentAmount < 100.0) {
// top up before ordering
}
// every denomination in an order must be priced in this currency
$currency = $info->currencyIsoCode; // e.g. "EUR"
use Amilon\Enum\AmilonErrorCode;
use Amilon\Exception\AmilonExceptionInterface;
use Amilon\Exception\ApiRequestException;
try {
$order = $client->makeOrder($request);
} catch (ApiRequestException $e) {
if (AmilonErrorCode::INSUFFICIENT_CONTRACT_CREDIT === $e->errorCode) {
// top up the contract and retry
}
if ($e->isTransient()) {
// 0000 / 0500 — safe to retry with the same input
}
} catch (AmilonExceptionInterface $e) {
// any other failure originating from the Amilon integration
}
// src/Amilon/AmilonClientFactory.php
namespace App\Amilon;
use Amilon\Dto\CredentialDto;
use Amilon\Enum\Environment;
use Amilon\Service\AmilonClient;
use Amilon\Service\AmilonClientFactory as LibraryAmilonClientFactory;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
final class AmilonClientFactory
{
public function __construct(
#[Autowire('%env(AMILON_USERNAME)%')]
private readonly string $username,
#[Autowire('%env(AMILON_PASSWORD)%')]
private readonly string $password,
#[Autowire('%env(AMILON_CLIENT_ID)%')]
private readonly string $clientId,
#[Autowire('%env(AMILON_CLIENT_SECRET)%')]
private readonly string $clientSecret,
#[Autowire('%env(AMILON_AUTH_DOMAIN)%')]
private readonly string $authDomain,
#[Autowire('%env(AMILON_WEB_DOMAIN)%')]
private readonly string $webDomain,
#[Autowire('%env(AMILON_WEB_DOMAIN_V2)%')]
private readonly string $webDomainV2,
#[Autowire('%env(AMILON_CONTRACT_ID)%')]
private readonly string $contractId,
#[Autowire('%env(AMILON_ENVIRONMENT)%')] // "staging" or "production"
private readonly string $environment,
) {
}
public function create(): AmilonClient
{
return LibraryAmilonClientFactory::create(new CredentialDto(
username: $this->username,
password: $this->password,
clientId: $this->clientId,
clientSecret: $this->clientSecret,
authDomain: $this->authDomain,
webDomain: $this->webDomain,
webDomainV2: $this->webDomainV2,
contractId: $this->contractId,
environment: Environment::from($this->environment),
));
}
}
// src/Controller/GiftCardOrderController.php
namespace App\Controller;
use Amilon\Dto\Request\CreateOrderRequestDto;
use Amilon\Exception\AmilonExceptionInterface;
use Amilon\Service\AmilonClient;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Attribute\Route;
final class GiftCardOrderController
{
public function __construct(
private readonly AmilonClient $amilonClient,
) {
}
#[Route('/gift-cards/orders', name: 'gift_card_order_create', methods: ['POST'])]
public function __invoke(Request $request): JsonResponse
{
$retailerId = (string) $request->request->get('retailerId');
$quantity = (int) $request->request->get('quantity', 1);
$price = (float) $request->request->get('price');
$externalOrderId = 'order-' . bin2hex(random_bytes(8));
try {
$order = $this->amilonClient->makeOrder(
CreateOrderRequestDto::singleLineWithPrice($externalOrderId, $retailerId, $quantity, $price),
);
} catch (AmilonExceptionInterface $amilonExceptionInterface) {
// InvalidOrderRequestException -> bad input (400);
// AuthenticationException / ApiRequestException -> upstream failure (502)
return new JsonResponse(['error' => $amilonExceptionInterface->getMessage()], 502);
}
return new JsonResponse([
'externalOrderId' => $order->externalOrderId,
'status' => $order->orderStatus,
'vouchers' => array_map(
static fn ($voucher): string => $voucher->voucherLink,
$order->vouchers,
),
]);
}
}
Amilon\Dto\Response\MerchantDenominationCollectionDto {
// iterable + countable: ->all() ->count() ->isEmpty()
-merchants: array:12 [
0 => Amilon\Dto\Response\MerchantDenominationsDto {
+code: "f72c8dc7-8feb-4dad-bf66-39c8ed238a2b" // <- retailerId for makeOrder()
+country: "Spain"
+countryIsoAlpha3: "ESP"
+name: "Carrefour"
+shortDescription: "Carrefour es una cadena de distribución multinacional..."
+longDescription: "<p>Carrefour es una cadena de distribución...</p>"
+imageUrl: "https://eurob2b.amilon.eu/b2bfiles/retailers/f72c8dc7-.../logo/a6fd150c.png"
+slug: "carrefour-esp"
+currency: "Euro"
+currencySymbol: "€"
+rebateTypeName: "Sconto fisso per Retailer"
+vatValue: 0.0
+vatValueName: "FC IVA art. 6-quater"
+denominations: array:7 [
0 => Amilon\Dto\Response\DenominationDto {
+code: "911d5af7-419b-ed11-b820-005056a53626"
+activationDate: DateTimeImmutable @1674497920 { 2023-01-23 18:18:40.0 UTC (+00:00) }
+imageUrl: "https://eurob2b.amilon.eu/b2bfiles/products/8f42058d-.../logo/d1ded420.png"
+rangeMin: null
+rangeMax: null
+step: null
+discountValue: 0.01
+prices: array:1 [
0 => Amilon\Dto\Response\DenominationPriceDto {
+price: 20.0
+netPrice: 19.8
}
]
+image136x86: "" +image461x292: "" +image200x200: "" +image300x190: "" +image560x292: ""
// the image* sizes are "" here — only getDenominationsComplete() fills them
// isFixed() => true isVariable() => false hasContractPriceOverride() => false
}
// 6 more fixed denominations: 100.0, 10.0, 25.0, 50.0, 5.0, 150.0
]
+extendedContent: null // only getDenominationsComplete() fills this
}
// 11 more merchants
]
}
// isVariable() — open span, empty prices; any multiple of step in [rangeMin, rangeMax] is orderable
Amilon\Dto\Response\DenominationDto {
+code: "68675d82-979f-f011-aa09-005056841cb3"
+rangeMin: 5.0
+rangeMax: 500.0
+step: 5.0
+discountValue: 0.02
+prices: []
}
// hasContractPriceOverride() — no range, an explicit contract-fixed set of values
Amilon\Dto\Response\DenominationDto {
+code: "68675d82-979f-f011-aa09-005056841cb3"
+rangeMin: null
+rangeMax: null
+step: null
+discountValue: null
+prices: array:7 [
0 => Amilon\Dto\Response\DenominationPriceDto { +price: 5.0 +netPrice: 5.0 }
1 => Amilon\Dto\Response\DenominationPriceDto { +price: 10.0 +netPrice: 10.0 }
// 25.0, 50.0, 100.0, 250.0, 500.0
]
}
Amilon\Dto\Response\ProductCollectionDto {
// iterable + countable: ->all() ->count() ->isEmpty()
-products: array:84 [
0 => Amilon\Dto\Response\ProductDto {
+productCode: "911d5af7-419b-ed11-b820-005056a53626" // denomination code; NOT unique per value
+merchantCode: "f72c8dc7-8feb-4dad-bf66-39c8ed238a2b" // == MerchantDenominationsDto->code
+name: "Carrefour - 20,00 €" // synthesised
+price: 20.0
+imageUrl: "https://eurob2b.amilon.eu/b2bfiles/products/8f42058d-.../logo/d1ded420.png" // denomination art, merchant-logo fallback
+active: true // always true
+visible: true // always true
+netPrice: 19.8
+discountValue: 0.01
+currency: "Euro"
+currencySymbol: "€"
+rangeMin: null
+rangeMax: null
+step: null
+activationDate: DateTimeImmutable @1674497920 { 2023-01-23 18:18:40.0 UTC (+00:00) }
// ── legacy merchant block: copied onto every row from the parent MerchantDenominationsDto,
// the same fields V1's product row carried ──
+merchantName: "Carrefour"
+countryIsoAlpha3: "ESP" // == MerchantCountryISOAlpha3
+merchantCountry: "Italy" // == MerchantCountry
+merchantImageUrl: "https://eurob2b.amilon.eu/b2bfiles/retailers/f72c8dc7-.../logo/aeab1a64.png" // pure merchant logo
+merchantShortDescription: "Carrefour, la spesa quotidiana e molto altro."
+merchantLongDescription: "<p>...</p>" // HTML
+merchantSlug: "carrefour-ita"
+rebateTypeName: "Sconto fisso per Retailer"
+vatValue: 0.0 // int % as float
+vatValueName: "FC IVA art. 6-quater"
+productType: "Voucher" // constant (V2 has no product type)
+art100: false // always false (V2 dropped the flag)
// isVariablePriced() => false
}
// a variable denomination collapses to ONE row: price == rangeMin, rangeMin/rangeMax/step
// set, netPrice 0.0, isVariablePriced() => true
]
}