Get started with docs
How it works
- 1. Get WooCommerce order
- 2. Sync WooCommerce order items into eHDM
- 3. Get prepared items from eHDM storage
- 4. Call
$eHDM->print()with the requiredcurrencyargument and amounts in that currency - 5. Receive the fiscal receipt response with amounts in AMD
Currency and conversion to AMD
The currency argument is required for every print() call, including AMD receipts. Supported currencies are AMD, USD, EUR and RUB. Numeric currency codes are also accepted as strings: '051', '840', '978' and '643', respectively.
Foreign-currency receipt printing is disabled by default, including after updating an existing site. To enable it, open Փոխարժեքներ (Exchange rates), select Արտարժույթով պատվերների կտրոնները տպել ՀՀ դրամով and click the Save button beside that checkbox. While this setting is off, new AMD receipts continue to work, but new USD, EUR and RUB receipts are not printed. Viewing, emailing, copying and returning existing receipts remain available. Retrieving rates or enabling their automatic updates does not enable foreign-currency receipt printing.
- For WooCommerce orders, use
$currency = (string) $order->get_currency();. Do not use the current store currency for an existing order. - Pass all payment amounts, item prices and monetary discounts in the same source currency specified by
currency. Do not convert them to AMD before callingprint(). - AMD requests keep their monetary values unchanged. For USD, EUR and RUB, conversion happens once inside
print(), immediately before the fiscal print request. - The conversion uses the currently selected saved rate at print time, not the rate at order creation or payment time. Printing does not fetch a new rate from the bank.
- Item quantities and percentage discounts are not converted. Monetary discounts are converted:
discountType = 2or4, andadditionalDiscountType = 16. Percentage types1and8retain their values. - For foreign-currency goods receipts (
mode = 2), the AMD total is calculated from the converted item prices, quantities and discounts. Monetary conversion uses two decimal places, so the result can differ slightly from directly converting the order total. Mixed payment amounts are allocated proportionally to this AMD total. - For foreign-currency receipts without items (
mode = 3), the payment amount is converted directly to AMD. The original payment or WooCommerce order is not changed.
Configure rates in the eHDM dashboard under Փոխարժեքներ (Exchange rates). Choose cba.am or a manual rate for each foreign currency. A rate is expressed as AMD per one unit of the source currency. For example, at an illustrative rate of 360.93, a payment of 10 USD becomes 3,609.30 AMD.
Automatic CBA updates are disabled by default. Enable Ինքնաշխատ թարմացում and save the schedule to turn them on. The default scheduled time is 16:15, Asia/Yerevan, independent of the WordPress site timezone. The manual CBA refresh button remains available when automatic updates are off. Without automatic updates, printing uses the selected saved rate; if no valid rate is available, the fiscal print request is not sent. WP-Cron execution depends on site traffic or a server cron configuration.
After successful printing, the original currency, original amount and applied rate are saved with the receipt. Later rate changes do not recalculate that receipt. The Tax Service response and its QR payment amounts are in AMD; the conversion snapshot is stored in eHDM history, not added as new fields to the Tax Service response.
Local validation errors return code = -3 with an error_code and errorMessage. Examples include currency_required, foreign_currency_print_disabled, unsupported_currency and rate_unavailable. Check the result before treating the receipt as printed.
Supported scenarios
- Card payment — full payment by card
- Cash payment — full payment in cash
- Mixed payment — part by card, part in cash
- Partial payment — payment goes into
partialAmount - Prepayment usage — payment goes into
prePaymentAmount
Base setup for the WooCommerce examples. The amount and prepared items must use the order currency:
$eHDM = apply_filters( 'planetstudio-payment-gateway-ehdm', null );
$wc_order_id = 2708;
$order = wc_get_order( $wc_order_id );
$amount = (float) $order->get_total();
$currency = (string) $order->get_currency();
$order_id = '3b2a2c1b-a850-4edd-abf9-bb2a36e1078a';
$eHDM->getWoocommersOrderItems( $wc_order_id );
$items = $eHDM->getWoocommersOrderItemsFromDB( $wc_order_id );
The response examples for Scenarios 1-5 illustrate AMD receipts with a total of 1 AMD, not fixed results for order 2708. Actual identifiers and totals come from the Tax Service. Foreign-currency orders produce AMD receipt amounts after conversion.
Scenario 1: Card payment
$result = $eHDM->print(
array(
'orderId' => $order_id,
'wc_orderId' => $wc_order_id,
'currency' => $currency,
'cardAmount' => $amount,
'cashAmount' => 0,
'partialAmount' => 0,
'prePaymentAmount' => 0,
'cashierId' => (int) get_option( 'ehdm_default_cashierId', 1 ),
'mode' => 2,
'partnerTin' => null,
'items' => $items,
)
);
Expected result:
{
"code": 0,
"message": "OK",
"errorMessage": null,
"result": {
"receiptId": "824",
"crn": "52014201",
"sn": "2FECD1F8",
"tin": "00493113",
"taxpayer": "...",
"address": "...",
"time": 1774363086000,
"fiscal": "30247466",
"total": 1,
"change": 0,
"qr": "TIN: ..., TOTAL_CASH: 0, TOTAL_NONCASH: 1, PREP_USAGE: 0, PARTIAL: 0, TOTAL: 1"
}
}
Scenario 2: Cash payment
$result = $eHDM->print(
array(
'orderId' => '',
'wc_orderId' => $wc_order_id,
'currency' => $currency,
'cardAmount' => 0,
'cashAmount' => $amount,
'partialAmount' => 0,
'prePaymentAmount' => 0,
'cashierId' => (int) get_option( 'ehdm_default_cashierId', 1 ),
'mode' => 2,
'partnerTin' => null,
'items' => $items,
)
);
Expected result:
{
"code": 0,
"message": "OK",
"errorMessage": null,
"result": {
"receiptId": "825",
"crn": "52014201",
"sn": "2FECD1F8",
"tin": "00493113",
"taxpayer": "...",
"address": "...",
"time": 1774363177000,
"fiscal": "46682581",
"total": 1,
"change": 0,
"qr": "TIN: ..., TOTAL_CASH: 1, TOTAL_NONCASH: 0, PREP_USAGE: 0, PARTIAL: 0, TOTAL: 1"
}
}
Scenario 3: Mixed payment
$card_amount = round( $amount * 0.6, 2 );
$cash_amount = round( $amount - $card_amount, 2 );
$result = $eHDM->print(
array(
'orderId' => $order_id,
'wc_orderId' => $wc_order_id,
'currency' => $currency,
'cardAmount' => $card_amount,
'cashAmount' => $cash_amount,
'partialAmount' => 0,
'prePaymentAmount' => 0,
'cashierId' => (int) get_option( 'ehdm_default_cashierId', 1 ),
'mode' => 2,
'partnerTin' => null,
'items' => $items,
)
);
Expected result:
{
"code": 0,
"message": "OK",
"errorMessage": null,
"result": {
"receiptId": "826",
"crn": "52014201",
"sn": "2FECD1F8",
"tin": "00493113",
"taxpayer": "...",
"address": "...",
"time": 1774363289000,
"fiscal": "25105467",
"total": 1,
"change": 0,
"qr": "TIN: ..., TOTAL_CASH: 0.4, TOTAL_NONCASH: 0.6, PREP_USAGE: 0, PARTIAL: 0, TOTAL: 1"
}
}
Scenario 4: Partial payment
$result = $eHDM->print(
array(
'orderId' => $order_id,
'wc_orderId' => $wc_order_id,
'currency' => $currency,
'cardAmount' => 0,
'cashAmount' => 0,
'partialAmount' => $amount,
'prePaymentAmount' => 0,
'cashierId' => (int) get_option( 'ehdm_default_cashierId', 1 ),
'mode' => 2,
'partnerTin' => null,
'items' => $items,
)
);
Expected result:
{
"code": 0,
"message": "OK",
"errorMessage": null,
"result": {
"receiptId": "827",
"crn": "52014201",
"sn": "2FECD1F8",
"tin": "00493113",
"taxpayer": "...",
"address": "...",
"time": 1774363388000,
"fiscal": "35778064",
"total": 1,
"change": 0,
"qr": "TIN: ..., TOTAL_CASH: 0, TOTAL_NONCASH: 0, PREP_USAGE: 0, PARTIAL: 1, TOTAL: 1"
}
}
Scenario 5: Prepayment usage
$result = $eHDM->print(
array(
'orderId' => $order_id,
'wc_orderId' => $wc_order_id,
'currency' => $currency,
'cardAmount' => 0,
'cashAmount' => 0,
'partialAmount' => 0,
'prePaymentAmount' => $amount,
'cashierId' => (int) get_option( 'ehdm_default_cashierId', 1 ),
'mode' => 2,
'partnerTin' => null,
'items' => $items,
)
);
Expected result:
{
"code": 0,
"message": "OK",
"errorMessage": null,
"result": {
"receiptId": "828",
"crn": "52014201",
"sn": "2FECD1F8",
"tin": "00493113",
"taxpayer": "...",
"address": "...",
"time": 1774363457000,
"fiscal": "10900220",
"total": 1,
"change": 0,
"qr": "TIN: ..., TOTAL_CASH: 0, TOTAL_NONCASH: 0, PREP_USAGE: 1, PARTIAL: 0, TOTAL: 1"
}
}
Prepayment note
Receiving a prepayment and using an existing prepayment are different scenarios. The prepayment-usage example above prints a goods receipt with mode = 2, an item list and the used amount in prePaymentAmount.
- Goods receipts (
mode = 2) use an item list, including the prepayment-usage example above. - To receive a prepayment without goods (
mode = 3), pass the received payment incardAmountorcashAmount. - Do not use
prePaymentAmountas a substitute for a newly received card or cash payment. The fiscal scenario must match the actual transaction.
Prepayment without items
The following example receives a new non-cash prepayment of 1 AMD, without goods. PayLink, PayForm and WPForms use this itemless prepayment-style flow. Replace the example bank order ID with the actual payment identifier.
$result = $eHDM->print([
'orderId' => 'BANK-ORDER-ID',
'wc_orderId' => 0,
'currency' => 'AMD',
'cardAmount' => 1,
'cashAmount' => 0,
'partialAmount' => 0,
'prePaymentAmount' => 0,
'cashierId' => 1,
'mode' => 3,
'partnerTin' => null,
'items' => [],
]);
Items source
You can provide items for print() in two different ways depending on your flow.
- 1. From WooCommerce order (real-time)
Use this method when you want to generate items directly from the WooCommerce order.
$items = $eHDM->getWoocommersOrderItems( $wc_order_id );
This will build the items list from the current order data without relying on previously saved receipts.
- 2. From eHDM prepared items storage
Use this method when items were already prepared and stored earlier.
$items = $eHDM->getWoocommersOrderItemsFromDB( $wc_order_id );
This reads the previously prepared WooCommerce items, not the fiscal receipt returned by the Tax Service. The prepared items remain in the source order currency. Pass that same currency to print(); do not convert or relabel the cached prices as AMD.
Custom items list
If you are not using WooCommerce order items, you can pass a custom items array manually.
$items = array(
array(
'adgCode' => '9205',
'dep' => 1,
'goodCode' => '9205-13',
'goodName' => 'երգեհոն',
'quantity' => 2,
'unit' => 'հատ',
'price' => 25000,
'additionalDiscount' => null,
'additionalDiscountType' => null,
'discount' => null,
'discountType' => null,
),
);
$result = $eHDM->print(
array(
'orderId' => 'CUSTOM-ORDER-1',
'wc_orderId' => 0,
'currency' => 'AMD',
'cardAmount' => 50000,
'cashAmount' => 0,
'partialAmount' => 0,
'prePaymentAmount' => 0,
'cashierId' => 1,
'mode' => 2,
'partnerTin' => null,
'items' => $items,
)
);
Receipt returns
Use the return flow to create a fiscal return receipt for a previously issued receipt. First request the original receipt information, then create the return receipt using the returned item identifiers and quantities.
1. Get return information
$receipt_id = 824;
$return_info = $eHDM->getReturnedReceiptInfo( $receipt_id );
if ( ! is_array( $return_info ) || 0 !== (int) ( $return_info['code'] ?? -1 ) ) {
// The Tax Service did not return information for this receipt.
return;
}
$receipt = $return_info['result'];
$items = $receipt['items'] ?? array();
The result contains the original receipt data. For goods receipts, result.items contains the returnable items. Use each item’s receiptProductId and the quantity to be returned when creating the return receipt.
2. Print a return receipt
// Return one unit of the first item from the original receipt.
$return_item_list = array(
array(
'receiptProductId' => (int) $items[0]['receiptProductId'],
'quantity' => 1,
),
);
$result = $eHDM->printReturnReceipt(
array(
'receiptId' => $receipt_id,
'cardAmountForReturn' => 0,
'cashAmountForReturn' => 5000,
'returnItemList' => $return_item_list,
)
);
For a card refund, set cardAmountForReturn and leave cashAmountForReturn as 0. For a cash refund, do the opposite. The refund amount must match the selected return items and must not exceed the original receipt amount.
Return amounts are in AMD. Use the amounts and item data from the original fiscal receipt returned by getReturnedReceiptInfo(). Do not convert them again using the current rate. The currency argument is required by print(), not by printReturnReceipt().
For receipts without goods, such as a prepayment receipt, use the data returned by getReturnedReceiptInfo() and pass an empty returnItemList only when the Tax Service response does not contain returnable items.
Return result
{
"code": 0,
"message": "OK",
"errorMessage": null,
"result": {
"receiptId": "829",
"fiscal": "...",
"total": 5000,
"qr": "TIN: ..., TOTAL_CASH: 5000, TOTAL_NONCASH: 0, ..."
}
}
A successful response has code = 0. The new return receipt identifier is available in result.receiptId, and its original QR data is in result.qr. When generating a QR image from a stored return receipt, pass the internal receipt_row_id to getReceiptQR() as described below to select that exact record.
Return parameters
| Parameter | Type | Description |
|---|---|---|
receiptId | int | Original fiscal receipt ID to return. |
cardAmountForReturn | double | Amount in AMD returned to a card or another non-cash payment method. |
cashAmountForReturn | double | Amount in AMD returned in cash. |
returnItemList | array | Items to return. Each item contains receiptProductId from getReturnedReceiptInfo() and the return quantity. |
Notes
- Always call
getReturnedReceiptInfo()beforeprintReturnReceipt(). - Do not create
receiptProductIdvalues manually; use the values returned by the Tax Service. - A return receipt is a new fiscal receipt and has its own receipt ID and QR data.
Receipt QR
You can generate a QR code image for a stored receipt using getReceiptQR(), or retrieve its original fiscal QR string using getReceiptQRData(). Both methods accept an optional internal receipt_row_id to select an exact stored receipt.
Usage
getReceiptQR( $receiptId, $url = false, $receipt_row_id = 0 )
getReceiptQRData( $receiptId, $receipt_row_id = 0 )
| Argument | Type | Description |
|---|---|---|
receiptId | int|string | Required fiscal receipt number returned by the Tax Service as result.receiptId. This number is not globally unique and may repeat for different registers or test/live modes. |
url | bool | Optional, for getReceiptQR() only. Pass true to return the image URL; the default false returns an HTML image tag. |
receipt_row_id | int | Optional; defaults to 0. The internal id of the corresponding record in the eHDM receipts database table. Pass a positive ID to select that exact record. It is not a fiscal receipt number, a WooCommerce order ID or a PSPG order number. |
Use receipt_row_id whenever the exact stored record is known. Take it from the corresponding record’s id in {WordPress table prefix}ehdm_receipts. The fiscal number must also match that record. The print() response does not include this internal database ID; do not use result.receiptId in its place.
For example, if the required stored record has internal id = 1234 and fiscal receiptId = 824:
$receipt_id = 824;
$receipt_row_id = 1234; // Internal ID of the exact stored receipt; illustrative only.
$url = $eHDM->getReceiptQR( $receipt_id, true, $receipt_row_id );
$html = $eHDM->getReceiptQR( $receipt_id, false, $receipt_row_id );
$qr_data = $eHDM->getReceiptQRData( $receipt_id, $receipt_row_id );
$qr_data contains the original qr string from the stored Tax Service response, not an image URL. Calls without receipt_row_id remain supported when the fiscal receipt number can be resolved unambiguously:
$url = $eHDM->getReceiptQR( 824, true );
Returns QR code image URL.
$html = $eHDM->getReceiptQR( 824 );
Returns ready-to-use HTML image tag.
$qr_data = $eHDM->getReceiptQRData( 824 );
Returns the original fiscal QR string, provided the receipt can be resolved unambiguously.
Example result
https://api.qrserver.com/v1/create-qr-code/?data=...&size=300x300
<img class="ehdm-qr" src="https://api.qrserver.com/..." alt="eHDM QR Code">
How it works
- Loads the exact receipt by
receiptIdandreceipt_row_idwhen a positive internal ID is provided. Without it, resolves the fiscal number only when there is no ambiguity between different receipts. - Extracts
qrfield - Encodes data into QR format
- Generates QR image via external service
Notes
receiptIdmust exist in eHDMgetReceiptQR()returnsfalseif the receipt is missing, ambiguous or has no QR data.getReceiptQRData()returns an empty string in those cases.- If
receipt_row_idis provided but does not matchreceiptId, the methods do not fall back to another record. Old receipts can still be selected by their exact internal ID after a register or test/live setting change. - A failed QR lookup does not mean fiscal printing failed. Do not print another receipt merely because QR lookup returned no result.
- QR contains full fiscal information
- You can use URL or ready HTML depending on your needs
Discount usage
Item discounts are passed through discount/discountType and additionalDiscount/additionalDiscountType. Monetary discount values must use the source currency; percentage values must remain percentages.
- 1. Product discount / discount
- % PERCENT — percentage value, discountType = 1
- PRICE (source currency) — discount applied per product / unit, discountType = 2. Calculation: (initial_price – discount) * quantity
- TOTAL (source currency) — discount applied to the total quantity, discountType = 4. Calculation: (price * quantity) – discount
- 2. Additional product discount / additionalDiscount
additionalDiscountType = 16— an additional monetary discount, in the source currency, subtracted from the line total after the product discount.additionalDiscountType = 8— an additional percentage discount applied to the line total after the product discount.
What the response means
code = 0— successmessage = OK— receipt was printed successfullyerrorMessage = null— no errorresult.receiptId— receipt IDresult.fiscal— fiscal numberresult.total— total fiscal receipt amount in AMDresult.change— change amount in AMDresult.qr— fiscal QR string with payment breakdown
QR fields
TOTAL_CASH— cash amountTOTAL_NONCASH— card amountPREP_USAGE— used prepayment amountPARTIAL— partial payment amountTOTAL— final total
Notes
- mode = 2 – Goods mode / 3 – Prepayment mode
orderIdidentifies the bank payment in card and mixed scenarios. It is not the PSPG order number; usepspgOrderNumberfor that.- WooCommerce items must be prepared first via
getWoocommersOrderItems() - Then load them via
getWoocommersOrderItemsFromDB() - All receipts are stored inside eHDM
Parameters
| Parameter | Type | Description |
|---|---|---|
currency | string | Required source currency: AMD, USD, EUR or RUB. Numeric codes must also be passed as strings. All payment amounts, prices and monetary discounts use this currency. |
orderId | string | Bank payment ID, when applicable. Can be empty for cash WooCommerce orders. |
wc_orderId | int | WooCommerce order ID, when applicable. |
pspgOrderNumber | int | PSPG order number, when applicable. Separate from the bank payment ID. |
cardAmount | double | Non-cash (card) payment amount in the source currency |
cashAmount | double | Cash payment amount in the source currency |
partialAmount | double | Partial payment amount in the source currency |
prePaymentAmount | double | Used prepayment amount in the source currency |
cashierId | int | Cashier ID |
mode | int | Receipt mode: 2 – Goods / 3 – Prepayment |
partnerTin | string | Buyer tax identification number (TIN) |
items | array | List of goods. Prices and monetary discounts use the source currency. Required for goods receipts. |
