Credits
Hyvä POS is in closed beta
Hyvä POS is currently in a closed beta (pilot phase) with a small group of merchants. It is not yet generally available: the App Store release follows the pilot, and features and configuration may still change - possibly in backwards-incompatible ways - before the general release. Want to take part? Sign up at hyva.io/pos.
| Method | Route | Purpose |
|---|---|---|
GET |
/V1/pos/credits/balance/:customerId |
All balances: store credit, gift cards, reward points |
GET |
/V1/pos/credits/giftcard/:code |
Validate a gift-card code |
POST |
/V1/pos/credits/apply |
Apply credit to an order (legacy; see note below) |
POST |
/V1/pos/credits/refund |
Refund an amount back to a credit balance |
All routes require the Hyva_Pos::locations ACL resource. The service aggregates every registered credit provider - a provider is a bridge module implementing CreditProviderInterface (Adobe Commerce store credit and gift cards, Amasty, or your own), registered with the CreditProviderPool via di.xml.
Get Balances
GET /V1/pos/credits/balance/:customerId - all credits for a customer across every active provider. The POS calls it when a customer is attached to the cart, to show redeemable balances at the register.
Response - one row per credit. Currency-based credits (store credit, gift cards) carry balance in currency and leave the points fields null; point-based credits carry balance in points plus the conversion fields:
[
{
"type": "store_credit",
"provider": "adobe_commerce",
"label": "Store Credit",
"balance": 25.0,
"currency": "EUR",
"code": null,
"is_redeemable": true,
"currency_value": null,
"points_per_currency": null,
"expires_at": null,
"details": null
},
{
"type": "loyalty_points",
"provider": "adobe_commerce",
"label": "Reward Points",
"balance": 480.0,
"currency": null,
"code": null,
"is_redeemable": true,
"currency_value": 4.8,
"points_per_currency": 100.0,
"expires_at": "2026-12-31T00:00:00+00:00",
"details": null
}
]
Error behavior: a provider that throws is skipped and the remaining providers still report - partial results are better than none. The route itself only fails on auth or transport errors.
Check a Gift Card
GET /V1/pos/credits/giftcard/:code - validates a gift-card code through the provider that declares the gift_card feature. The POS calls it when the cashier scans or types a card at checkout. URL-encode the code segment.
Response: a single balance row in the same shape as above, with type: "gift_card" and code set to the card code.
Error behavior: 404 with Gift card with code "X" not found. when no gift-card provider is installed, the code does not exist, or the provider lookup fails.
Apply Credit
POST /V1/pos/credits/apply - applies an amount of credit to an order through a named provider.
The app no longer calls this after placing an order
Historically the POS called apply after order placement to debit the customer's balance. All three wallet types (store credit, gift cards, reward points) are now debited by the module's own order-save dispatchers, keyed on the order's extension attributes - the debit happens inside the order save, atomically, and the dispatchers own the single authoritative debit. The endpoint remains for integrations and older clients; current bridges serve it validate-only.
Request - orderId is the order entity id, sent as a string:
{
"orderId": "2043",
"creditType": "gift_card",
"provider": "adobe_commerce",
"amount": 25.0,
"code": "GIFT-2H4K-9QXR",
"customerId": 1287
}
code is required for gift cards, null for store credit and points. customerId may be null for guest gift-card redemption.
Response - business failures come back as 200 with success: false and an error_message; the endpoint does not throw for a zero amount, an unknown provider, or a provider-side rejection:
{
"success": true,
"amount_applied": 25.0,
"remaining_balance": 50.0,
"transaction_id": "gc-88213",
"error_message": null
}
Refund to Credit
POST /V1/pos/credits/refund - pushes an amount back onto a credit balance. Called by the RMA settle paths when a return resolves to store credit, and available to integrations that need to hand value back outside a return.
Request:
{
"orderId": "2043",
"creditType": "store_credit",
"provider": "",
"amount": 25.0,
"customerId": 1287,
"reason": "Return 000000018 refunded to store credit",
"code": null
}
orderIdis the order entity id as a string - the server resolves it numerically to enforce the refund guard.- An empty
providermeans "the active provider for this credit type" - the RMA settle paths pass""because they have no provider code in hand. codecarries the gift-card code for gift-card refunds.
Response: same shape as apply - success, amount_applied, remaining_balance, transaction_id, error_message.
Over-Refund Guard
Credit issued back for an order is cumulative across all calls and may never exceed the order's total_refunded (which Magento itself caps at total_paid). The module tracks issued credit in a per-order ledger (pos_credit_refunded); credit issued through other paths (for example the memo-time refund-to-credit bridge) is folded into the same ledger. The guard fails closed:
- Amount exceeds the remaining refundable amount:
200withsuccess: falseandRefund amount X exceeds the remaining refundable amount Y for this order. - Order id cannot be resolved:
200withsuccess: falseandOrder "X" not found - cannot validate the refund amount. - No provider installed for the credit type:
200withsuccess: falseandNo credit provider is installed for "store_credit".
A ledger write failure after a successful provider refund is logged but does not fail the call - the provider's own transaction record is the authoritative trail.