=== HAfiscal for WooCommerce ===
Contributors: hostarena
Tags: fiscalization, serbia, receipt, e-invoice, ips qr
Requires at least: 5.8
Tested up to: 7.1
Requires PHP: 7.4
WC requires at least: 6.0
Stable tag: 1.10.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Serbian fiscal receipts and e-invoices for WooCommerce via HAfiscal, bank transfer / IPS QR payments confirmed automatically, product and stock sync.

== Description ==

When an order becomes "Processing" or "Completed", the plugin sends it to the HAfiscal Cloud API, which issues a fiscal receipt through the Serbian Tax Administration fiscal system (PFR). The receipt (number, QR code, journal, verification link) is stored on the order and shown:

* to the customer on the "Thank you" page and in "My account > Orders";
* in customer emails (the receipt is issued before the email is sent);
* on a public printable page;
* to the shop admin in an order meta box and an orders list column.

Other features:

* Customer type field: private person or company, with company name, tax ID (PIB) and company registration number (MB), validated by check digit. Company buyers get their details on the receipt. Compatible with "Serbian Addons for WooCommerce" (same field keys, no duplicate fields).
* Explicit mapping of every payment method to the fiscal payment type (bank transfer, card, instant payment, cash).
* Automatic retries via WP-Cron without duplicate receipts.
* Automatic storno when an order is refunded.
* Serbian e-invoices (eFaktura, SEF) for company buyers, sent through HAfiscal - see below.
* Bank transfer / IPS QR payment method confirmed automatically by HAfiscal - see below.
* Products and stock synced with HAfiscal - see below.

= Bank transfer / IPS QR payment =

A payment method "Uplata na račun / IPS QR" (bank transfer / IPS QR), available for RSD.

* At checkout the plugin opens a payment request in HAfiscal (one per order, idempotent). If HAfiscal cannot open it (no connection, company bank account missing), the customer sees a clear error and can choose another payment method.
* The order is set to "On hold" and the customer gets the NBS IPS QR code and the payment slip data (payee, account, amount, model 97 and reference number, purpose, payment code) on the "Thank you" page, in "My account > Orders" and in the "On hold" / invoice email (the QR image in emails is a signed link, because many email clients block embedded images).
* When HAfiscal matches the payment from the bank statement, it notifies the shop (webhook, HMAC-SHA256 signature checked with the webhook secret). A fully paid order is marked paid and the existing flow issues the fiscal receipt or e-invoice. Partial or over-payments only add an order note and an admin notice.
* Fallback: WP-Cron checks waiting orders every 15 minutes for a configurable number of days (default 14).
* Cancelling the order cancels the payment request.
* On the fiscal receipt this method is always mapped (default "Bank transfer"; can be changed to "Instant payment" in the payment method mapping).

= Products and stock =

Settings tab "Artikli i zalihe" (products and stock).

* Simple products and variations with a SKU are sent to HAfiscal when saved, or all at once with "Send all products" (batches of 100 in the background, progress shown). Products without a SKU are skipped and listed.
* Price with VAT (active price, sale price when valid) according to the store tax settings; VAT rate from the product tax class (editable table, default standard 20%, reduced 10%, zero 0%).
* Stock is managed in HAfiscal (selected warehouse). A paid order ("Processing"/"Completed") decreases it once, cancelled or refunded orders (full or per line) increase it. Stock changes in HAfiscal reach the shop through a webhook, the "Reconcile stock now" button and a daily check; these updates are never sent back. Quantities held by orders still waiting for payment are subtracted on the shop.
* Products list column and order notes show the sync status.

= eFaktura (SEF) =

E-invoices are a feature of the HAfiscal service: the plugin sends the invoice data to HAfiscal, and HAfiscal sends the e-invoice to SEF (the Serbian e-invoice system) on the merchant's behalf, using the SEF settings (SEF API key, environment, company data, bank account) stored in the merchant's HAfiscal account. The plugin itself never connects to SEF. The invoice also appears in HAfiscal among outgoing invoices, numbered by the company's HAfiscal numbering. An HAfiscal account with eFaktura enabled and set up is required; if it is not, HAfiscal's message is shown on the order and the buyer gets a fiscal receipt.

* Turned on with "Send e-invoices" ("Šalji eFakture") on the eFaktura settings tab; "Check connection" ("Proveri vezu") shows whether eFaktura is ready in HAfiscal.
* Only buyers marked as a company with a valid tax ID (PIB) whose company is registered on SEF get an e-invoice (HAfiscal checks the tax ID; the answer is kept for 24 hours). Private persons never get one.
* Invoice lines are the same as on the fiscal receipt (exact order total, shipping as a line, RSD only); VAT category and exemption code for 0% lines are configurable.
* Sent automatically when the order becomes "Processing" or "Completed" (configurable) or manually from the order screen and the "Send e-invoice" ("Pošalji eFakturu") bulk action. An already sent order is never sent again automatically; a new e-invoice for the same order requires explicit confirmation.
* Temporary errors are retried by WP-Cron with the same invoice reference, so no duplicates are created.
* Configurable fiscal receipt rules: company with e-invoice - no fiscal receipt (default), company without e-invoice - fiscal receipt, private person - fiscal receipt. A skipped receipt is recorded on the order with the reason and can still be issued manually.

An HAfiscal account with an API key is required (https://hafiskal.rs).

== Installation ==

1. Click "Connect to HAFiskal" ("Poveži sa HAFiskal") in WooCommerce > HAfiscal, log in to HAFiskal and approve the connection. The API key and the notification (webhook) secret are set up automatically - nothing to copy.
2. Map payment methods on the same page and save. Enable "Uplata na račun / IPS QR" in WooCommerce > Settings > Payments if you want bank transfer / IPS QR payments.
3. Installing: Plugins > Add New (search "HAfiscal") or upload the `hafiscal-for-woocommerce` folder to `wp-content/plugins` and activate it. If an older copy in the `finora-woocommerce` folder is installed, deactivate and delete it first; settings and receipts are kept.
4. Shops that already entered an API key manually: click "Set up notifications automatically" ("Podesi obaveštenja automatski"). Manual API key and webhook secret fields remain under "Advanced / manual setup" ("Napredno / ručno podešavanje").

== External services ==

This plugin connects to one external service, the HAfiscal Cloud API. Nothing is sent when the plugin is activated: no data leaves the site until the shop admin connects the shop to HAfiscal ("Connect to HAFiskal") or enters an HAfiscal API key. Without an API key the plugin makes no requests at all.

= HAfiscal Cloud API (HostArena) =

What it is: HAfiscal is a Serbian invoicing and fiscalization service provided by HostArena (https://hafiskal.rs). The plugin talks to the HAfiscal Cloud API at the address set in the plugin settings (default https://api.hafiskal.rs) and, only during "Connect to HAFiskal", sends the admin's browser to the HAFiskal web app (default https://dash.hafiskal.rs). https://api.ha.co.rs and https://fiscal.ha.co.rs are the former addresses of the same HAfiscal Cloud API and web app; installations that still have them saved are switched to the new addresses automatically on update.

What it is used for and what data is sent, and when:

* Fiscal receipts (fiscalization): when an order becomes "Processing" or "Completed" (or the admin clicks "Fiscalize"), and on WP-Cron retries of failed orders, the plugin sends the order ID and number, currency, order items (name, quantity, price with VAT, VAT rate, discounts), shipping and fees, the payment method mapped to the fiscal payment type and the amount. For company buyers only, it also sends the company name, tax ID (PIB), company registration number (MB), billing address, e-mail and phone so that the buyer appears on the receipt. No personal data is sent for private persons. When an order is refunded, the order ID and refunded amounts are sent to issue a refund (storno) receipt.
* Tax rates and company check: the plugin reads the company's VAT rates, business locations and company name/tax ID from HAfiscal (when fiscalizing and when the admin clicks "Test connection").
* E-invoices (eFaktura, only when "Send e-invoices" is turned on, and only for company buyers): before the first e-invoice to a buyer, the buyer's tax ID (PIB) is sent to check whether the company is registered on SEF (the answer is kept for 24 hours). When an e-invoice is sent (automatically on order status "Processing"/"Completed", or manually from the order screen or bulk action) the plugin sends the invoice data: buyer company name, tax ID (PIB), company registration number (MB), public sector ID (JBKJS, if entered), billing address, postcode, city and e-mail, order items (name, quantity, price with VAT, VAT rate, discounts), shipping, amounts, issue/delivery/due dates, the order number as the payment reference and the payment method. HAfiscal forwards this data to the Serbian e-invoice system SEF (Ministry of Finance of the Republic of Serbia) on the merchant's behalf, using the merchant's SEF settings stored in HAfiscal; the plugin never contacts SEF itself. "Refresh status" and "Check connection" ask HAfiscal for the invoice status and for whether eFaktura is ready.
* Payment requests (bank transfer / IPS QR): when a customer chooses the "Uplata na račun / IPS QR" payment method, the plugin sends the order ID, order total, currency, description ("Order #number"), the customer's name and e-mail and the expiry date to get the payment slip data and the NBS IPS QR code. While the order waits for payment, the plugin asks HAfiscal for the payment status (at most every 15 minutes, for the configured number of days) and cancels the request when the order is cancelled. HAfiscal sends payment notifications to the shop's webhook URL (/wp-json/hafiscal/v1/webhook); they are accepted only with a valid HMAC-SHA256 signature.
* Products and stock (only when enabled in the "Artikli i zalihe" tab): product data (SKU, name, price with VAT, VAT rate, barcode, category, short description, image URL, active flag) is sent when a product is saved or on "Send all products"; stock movements (SKU, quantity change, order or refund ID) are sent when an order is paid, cancelled or refunded; the list of warehouses and stock quantities is read on "Reconcile stock now" and once a day. No customer data is sent for products and stock.
* One-click connect ("Connect to HAFiskal"): when the admin clicks the button, the browser is sent to the HAFiskal web app with the shop URL, shop name, return URL and a random one-time state value. After the admin approves, the plugin sends the one-time code and the shop URL to the HAfiscal Cloud API (/v1/connect/exchange) and receives the API key, API address, webhook secret and the company name and tax ID. "Set up notifications automatically" sends the shop's webhook URL and the list of events.

* Service provider: HostArena, https://hafiskal.rs
* Terms of Service: https://hafiskal.rs/uslovi-koriscenja/
* Privacy Policy: https://hafiskal.rs/politika-privatnosti/

Data forwarded by HAfiscal to SEF (e-invoices only): SEF - Sistem e-faktura, the Serbian e-invoice system operated by the Ministry of Finance of the Republic of Serbia. Terms of use, privacy and legal documents: https://www.efaktura.gov.rs

= Links shown to customers =

The fiscal receipt contains a verification link and QR code issued by the Tax Administration of the Republic of Serbia (suf.purs.gov.rs). The plugin only shows this link; the customer's browser opens it only if the customer clicks it or scans the QR code. QR code images in e-mails are served from the shop itself.

== Changelog ==

= 1.10.0 =
* E-invoices (eFaktura) are now sent through HAfiscal: the plugin sends the invoice data to the HAfiscal Cloud API and HAfiscal sends the e-invoice to SEF with the SEF settings from the merchant's HAfiscal account. The plugin no longer connects to SEF and no longer stores SEF API keys or seller data (removed from existing installations on update).
* E-invoices also appear in HAfiscal among outgoing invoices.
* New "Send e-invoices" switch and "Check connection" button on the eFaktura tab; installations with a SEF API key entered keep e-invoices turned on.
* Readme: "External services" lists only the HAfiscal Cloud API, including the e-invoice data that HAfiscal forwards to SEF; the former HAfiscal addresses (api.ha.co.rs, fiscal.ha.co.rs) are documented.

= 1.9.0 =
* Styles and scripts are loaded only with wp_enqueue_* / registered handles from the assets folder; no inline style or script tags. The admin script is loaded only on the plugin's own screens (HAfiscal settings and order screens).
* Admin notices are shown only on the plugin's own screens (HAfiscal settings, orders, WooCommerce payment settings) and can be hidden; the "WooCommerce required" notice is shown only on the Plugins screen.
* Block checkout fields: removed the unsupported "inputmode" attribute (no notices with WP_DEBUG).
* WP-Cron events are scheduled on "init" instead of "plugins_loaded" (no early translation loading notice); fixed a PHP 8.4 deprecation notice.
* No requests to external services until the shop is connected to HAfiscal / an API key is entered.
* Readme: "External services" section with what is sent, when, and links to the Terms of Service and Privacy Policy.

= 1.8.1 =
* HAfiscal moved to the hafiskal.rs domain: default API address is now https://api.hafiskal.rs and the HAFiskal web address is https://dash.hafiskal.rs.
* On update, saved addresses equal to the old defaults (https://api.ha.co.rs, https://fiscal.ha.co.rs) are switched to the new ones automatically. Custom addresses are not changed. The old addresses keep working.

= 1.8.0 =
* One-click "Connect to HAFiskal" ("Poveži sa HAFiskal"): approve in HAFiskal, API key, API address and webhook secret are saved automatically, connection is tested; status box with company name and tax ID, "Reconnect" and "Disconnect".
* "Set up notifications automatically" for shops with a manually entered API key (creates or updates the webhook in HAFiskal and saves the secret).
* New setting "HAFiskal web address"; manual API key and webhook secret fields moved to "Advanced / manual setup". Keys and secrets are never shown or logged.

= 1.7.0 =
* New payment method "Uplata na račun / IPS QR": payment request in HAfiscal at checkout, IPS QR code and payment slip on the "Thank you" page, in My account and in emails, automatic confirmation by webhook (HMAC signature) with a 15-minute fallback check, partial/over-payment notes and admin notice, request cancelled with the order; mapped to "Bank transfer" on the fiscal receipt.
* Webhook endpoint /wp-json/hafiscal/v1/webhook and webhook secret setting.
* New settings tab "Artikli i zalihe": product sync by SKU (on save and in bulk), VAT rate per tax class, warehouse choice, stock movements for paid, cancelled and refunded orders, stock updates from HAfiscal (webhook, button, daily) without loops, products list column.

= 1.6.0 =
* Serbian e-invoices (eFaktura, SEF) for company buyers registered on SEF: exact order totals, automatic or manual sending, bulk action, safe retries.
* Fiscal receipt rules per buyer type (company with e-invoice, company without e-invoice, private person); skipped receipts are recorded with the reason.
* Fix: the retry cron no longer picks up refund objects.

= 1.5.0 =
* Prepared for WordPress.org: plugin slug and text domain "hafiscal-for-woocommerce". Existing installations: deactivate the old "finora-woocommerce" plugin, settings and receipts are kept.

= 1.4.1 =
* Plugin name "HAfiscal for WooCommerce", license header, English readme, translator comments.

= 1.4.0 =
* Customer type field (person / company) with company name, tax ID and registration number on checkout, registration and My account; compatible with Serbian Addons for WooCommerce.

= 1.3.0 =
* Explicit payment method mapping (bank transfer, card, instant payment, cash).

= 1.2.1 =
* Company buyer details on the receipt.

= 1.2.0 =
* Automatic retries via WP-Cron without duplicate receipts.

= 1.1.0 =
* Receipt shown to the customer, emails, refunds, HPOS compatibility and many fixes.

= 1.0.0 =
* First version.
