=== Affilyflow – Affiliate Tracking ===
Contributors: affilyflow
Tags: affiliate, affiliate marketing, woocommerce, tracking, referral, commission
Requires at least: 5.6
Tested up to: 6.6
Requires PHP: 7.2
Stable tag: 1.10.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Track affiliate-driven sales in WooCommerce and credit the right affiliate automatically. One-click connect — no code, no theme edits.

== Description ==

Affilyflow connects your WooCommerce store to your affiliate program and tracks every affiliate-driven sale automatically.

* **One-click connect** — install, paste your connect code, done. No code, no theme edits.
* **Reliable server-side attribution** — sales are recorded server-side on the order, so they aren't lost to ad-blockers.
* **Product-level data** — line items are sent so affiliates can see which products sell.
* **Privacy-first** — only an anonymous affiliate click id, order totals/line items and a one-way fingerprint of the billing email (to catch self-referrals) are sent. No customer names, email addresses or payment data.

How it works:

1. A visitor arrives through an affiliate's link (with `?afid=…`). The plugin stores the anonymous click id in a cookie.
2. When the order is placed, the sale is sent to Affilyflow (server-side) and credited to the right affiliate.
3. You see clicks, sales and commissions in your Affilyflow dashboard.

== Installation ==

1. Upload the plugin zip via **Plugins → Add New → Upload Plugin**, then **Activate**.
2. Go to **WooCommerce → Affilyflow**.
3. Paste your connect code from your Affilyflow dashboard (Installation → Website/script) and click **Forbind**.

That's it — tracking is now active.

== Frequently Asked Questions ==

= Do I need to edit my theme or add scripts? =
No. The plugin handles click capture and server-side conversion tracking automatically.

= Which data is sent to Affilyflow? =
The anonymous affiliate click id, the order id, order total (ex VAT), currency and line items – and, from 1.8.0, a SHA-256 fingerprint of the billing email. The email address itself is never sent. The fingerprint is only compared with the affiliate's own email to catch self-referrals, and is not stored. No names, addresses or payment data.

From 1.9.0 the plugin also sends a small daily heartbeat so Affilyflow can tell a quiet store from a broken connection: your store's key, the plugin version, the WooCommerce, WordPress and PHP versions, and your site address. No customer or order data. The heartbeat is removed when you deactivate the plugin.

= What happens when I refund an order? =
A full refund (or a cancelled or failed order) reverses the sale: no commission and no fee. From 1.10.0 a partial refund reduces the commission and the network fee in proportion – if a customer returns one of three items, you only pay for the two that were kept. If your program pays a fixed amount per sale, a partial refund does not change it. Only the refund amount, the order total, the refunded item totals (excl. VAT) and the refund id are sent. Once the refunds add up to the whole order, the sale is reversed as before.

= What is the commission calculated on? =
The value of the goods: the order's line items after discounts, excluding VAT and shipping. Payment or handling fees are left out; discounts given as a negative fee are taken off. Gift cards and store credit are a way of paying, but commission is never calculated on more than the order total excluding VAT.

= Works alongside other networks? =
Yes. From 1.10.0 the last affiliate click wins, across networks. If a customer clicks an Affilyflow link and later comes back through another network's link (Partner-Ads, Tradedoubler incl. Grow, Adtraction, Awin, impact.com, CJ, Rakuten, Admitad, or any link tagged `utm_medium=affiliate`), the plugin forgets the Affilyflow click and the order is not reported to Affilyflow. If the customer then clicks an Affilyflow link again, Affilyflow owns the sale again. You can switch this off under **WooCommerce → Affilyflow** ("Sidste klik vinder"); then the plugin behaves as before.

The other networks are recognised by the click parameter they add to your landing page: `pacid` / `pa-partnerid` (Partner-Ads), `tduid` (Tradedoubler), `at_gd` (Adtraction), `awc` (Awin), `im_ref` / `irclickid` / `irmp` (impact.com), `cjevent` (CJ), `ranSiteID` (Rakuten), `admitad_uid` (Admitad). Developers can change the list with the `affilyflow_other_network_params` filter (`'param' => 'network'`).

The plugin only decides about Affilyflow. To make the other networks follow the same rule, fire their thank-you pixels only when they own the last click. The winner is available as:

* the PHP function `affilyflow_last_network( $order )` – returns `affilyflow`, `partner-ads`, `tradedoubler`, `adtraction`, `awin`, `impact`, `cj`, `rakuten`, `admitad`, `other`, or an empty string when no affiliate click is known (filter: `affilyflow_last_network`);
* the first-party cookie `afw_last_network` (same values, readable by scripts and tag managers).

Example – only fire Partner-Ads' and Tradedoubler's pixels when they own the last click:

`add_action( 'woocommerce_thankyou', function ( $order_id ) {
    $network = function_exists( 'affilyflow_last_network' ) ? affilyflow_last_network( $order_id ) : '';
    if ( in_array( $network, array( '', 'partner-ads' ), true ) ) {
        // Partner-Ads' sale pixel here
    }
    if ( in_array( $network, array( '', 'tradedoubler' ), true ) ) {
        // Tradedoubler's conversion tag here
    }
}, 20 );`

An empty value means no click was recorded by this plugin (for example a click from before 1.10.0), so the network's own cookie decides as before. In Google Tag Manager, use a 1st-party cookie variable for `afw_last_network` as a condition on each network's trigger. Pixels that another plugin adds by itself (for example the Grow by Tradedoubler plugin, which reports from the thank-you page on its own) are not controlled by this setting – ask that network how to condition or deduplicate them.

= What happens if an affiliate buys through their own link? =
From 1.8.0 the sale is recorded as rejected with the reason "self-referral", so both you and the affiliate can see it. It costs you nothing: no commission and no fee.

== Changelog ==

= 1.10.0 =
* Partial refunds now count. When you refund part of an order in WooCommerce,
  the commission and the network fee are reduced in proportion. Before, only a
  full refund reached Affilyflow, so returning one item out of three left the
  whole commission standing. When the refunds add up to the whole order, the
  sale is reversed as before. A fixed commission per sale is not reduced by a
  partial refund.
* A refund of specific items reduces the commission by exactly those items
  (excl. VAT); a shipping-only refund leaves the commission as it is. A refund
  of an amount only (no items) reduces it by the same share of the order.
* Each refund is sent once, with its WooCommerce refund id, so it can never be
  counted twice.
* Last click wins across affiliate networks ("Sidste klik vinder", on by
  default). When a customer arrives through another network's link (Partner-Ads,
  Tradedoubler, Adtraction, Awin, impact.com, CJ, Rakuten, Admitad or
  `utm_medium=affiliate`) after an Affilyflow click, the Affilyflow click is
  forgotten and the order is not reported to Affilyflow, so you never pay two
  networks for the same sale. A new Affilyflow click wins again. Only real page
  views count – never wp-admin, REST, AJAX or cron. Can be switched off on the
  settings page.
* The network that owns the last click is available as
  `affilyflow_last_network( $order )`, in the cookie `afw_last_network` and on
  the order, so other networks' thank-you pixels can be fired only when they
  own the sale (see the FAQ). Developers can change the recognised parameters
  with the `affilyflow_other_network_params` filter.
* The commission basis is now calculated from the order lines themselves (line
  totals after discounts, excl. VAT and shipping). The result is the same as
  before for normal orders, but it no longer depends on a stored discount
  figure that another plugin may have changed, and discounts given as a
  negative fee are now taken off.

= 1.9.0 =
* Daily heartbeat: once a day the plugin tells Affilyflow that it is running,
  with the plugin, WooCommerce, WordPress and PHP versions. A store without
  orders for a while can now be told apart from a store where tracking has
  stopped (plugin deactivated, removed or blocked by the host), and we can
  reach out before sales are lost. No customer or order data is sent.
* The plugin version is now also sent when you connect, run the self-test and
  with each order.
* The settings page shows when the plugin last reached Affilyflow – and warns
  you if your host blocks the connection.
* Uninstalling now also removes the plugin key and the heartbeat schedule.

= 1.8.0 =
* Self-referrals are now caught automatically. When an affiliate buys through
  their own link, the sale is recorded as rejected – visible to you and the
  affiliate, with no commission and no fee. Before, the sale counted like any
  other until you noticed and rejected it.
* To do this, the plugin sends a one-way SHA-256 fingerprint of the billing
  email with the order. The email address itself never leaves your store, and
  Affilyflow only compares the fingerprint with the affiliate's own email.

= 1.7.1 =
* Auto-updates are now on by default when the plugin is first activated, so
  tracking fixes reach your store without you having to do anything. You can
  turn them off in the plugin list, and that choice is kept.
* The "Enable auto-updates" toggle now also shows when you are on the newest
  version.

= 1.7.0 =
* Commission is now calculated on the value of the goods: VAT and shipping are
  both left out. Shipping is a cost you front, not revenue an affiliate created,
  and every major affiliate network leaves it out of the basis. Until now you
  paid commission on shipping as well. The order value shown in Affilyflow is
  unchanged; only the basis for the commission has changed.
* Fixed: an order is now only credited to an affiliate through the click saved
  on the order at checkout. Before, changing an order's status in wp-admin could
  credit it to an affiliate link the admin had clicked in their own browser.

= 1.0.0 =
* Initial release: click capture + server-side WooCommerce conversion tracking, one-click connect.
