=== Flow Affiliate ===
Contributors: flowgroup
Tags: affiliate, woocommerce, affiliate marketing, conversion tracking, attribution
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.0.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Reports sign-ups, orders and refunds from your shop to Flow Affiliate, signed and server to server, so publishers are paid for the customers they bring.

== Description ==

Flow Affiliate is an affiliate network. A publisher shares a link such as `https://go.affiliate.yflow.io/a/k7qxa2mp`; Flow records the click and sends the visitor to your site with `fg_click` and `fg_ref` in the URL. This plugin keeps that click and tells Flow what the visitor did next.

* **Keeps the click** in a first-party cookie on your own domain: HttpOnly, SameSite=Lax, Secure on https. No JavaScript and no third-party cookies.
* **WooCommerce orders.** The click is copied onto the order at checkout, classic or block. When the order is paid, the plugin sends `user.registered`, which ties the customer to the publisher, and `purchase.completed`.
* **Refunds and cancellations** send `purchase.refunded`, which takes back what the order earned.
* **Publisher coupons.** Give a coupon a Flow publisher code and orders using it are attributed to that publisher, with or without a link. Other coupons are never sent as codes.
* **Reliable delivery** through Action Scheduler, retried for about two days, with a log of recent deliveries.
* **Without WooCommerce** it can send `user.registered` when someone who came through a link creates an account, and developers can send any event with one hook.

WooCommerce's order tables (HPOS) and the block checkout are supported.

= What is sent =

* An order with a click or a publisher coupon is paid (Processing or Completed): `user.registered` then `purchase.completed`, in one signed request.
* A paid order from a customer account Flow already knows: `purchase.completed`.
* A refund, full or partial, or a cancellation after payment: `purchase.refunded`, naming the order's `purchase.completed`.
* Any other order is not sent.

The amount commission is paid on is the order's line items after discounts, without tax, shipping or fees, in minor units (cents). The gross, tax, shipping, discount, items and coupon codes are in the event's metadata.

= Privacy =

Customers are named by pseudonymous ids only: `wc:<customer id>`, or `wc-guest:<order id>` for a guest. Names, email addresses, phone numbers and street addresses are never sent. Events carry the billing country, the products (id, SKU, name, quantity, amount) and coupon codes.

The cookie exists only for visitors who arrive through a Flow link. If your consent tool must allow it first, use the `flow_affiliate_set_cookie` filter.

== Installation ==

1. In WordPress, go to **Plugins → Add New Plugin → Upload Plugin**, choose `flow-affiliate-wordpress.zip` and activate it.
2. In Flow, open your program's setup, step 2 **Send events server to server**, and create a test key. Copy the secret: it is shown once.
3. Go to **Settings → Flow Affiliate**, paste the key id and the secret, keep the environment on **Test** and save.
4. Choose **Test connection**.
5. Open one of your program's links, place an order and mark it Processing. **Recent deliveries** shows it as sent.
6. Once Flow Group has approved your program, create a production key, paste it, switch the environment to **Production** and test again.

== Frequently Asked Questions ==

= Are partial refunds supported? =

Every refund is reported, with the refunded part of the commissionable amount in `amount` and `partial` in metadata. Flow currently takes back everything the purchase earned on any refund, partial or not. To report only full refunds:

`add_filter( 'flow_affiliate_send_refund', function ( $send, $refund, $order, $partial ) { return ! $partial; }, 10, 4 );`

= Why is a coupon code not sent as the publisher's code? =

Publishers pick their own codes, so a generic coupon such as SUMMER10 could match one, and every order using it would be paid to that publisher. Only a coupon you give a **Flow publisher code** (on the coupon's General tab) is sent, as that code.

= My pages are cached =

The cookie is set by PHP on the page the link lands on, and the plugin marks that response as not cacheable. A cache or CDN that serves URLs with `fg_click` from cache, or strips the parameter, never reaches PHP: exclude `fg_click` and `fg_ref` URLs from it.

= Can I keep the secret out of the database? =

Yes. Define it in `wp-config.php` and the settings page shows it as set there:

`define( 'FLOW_AFFILIATE_KEY_ID', 'fg_live_...' );`
`define( 'FLOW_AFFILIATE_SECRET', '...' );`

= How do I send my own events? =

`do_action( 'flow_affiliate_track', 'subscription.started', 'wp:' . $user_id, array( 'eventId' => 'subscription:' . $id, 'amount' => 999, 'currency' => 'USD' ) );`

The arguments are the event's fields: `eventId` (keep it stable, Flow deduplicates on it), `occurredAt`, `amount` in cents with `currency`, `reversesEventId`, `metadata`, `clickId` and `affiliateCode`. A `user.registered` without a click or code takes the visitor's cookie.

= Which filters are there? =

* `flow_affiliate_set_cookie( $set, $click )`: return false to not keep the click, for example before consent.
* `flow_affiliate_order_events( $events, $order )`: change the events of a paid order before they are queued.
* `flow_affiliate_send_refund( $send, $refund, $order, $partial )`: return false to not report a refund.

== Changelog ==

= 1.0.0 =
* First release: click cookie, WooCommerce orders, refunds and cancellations, publisher coupons, sign-ups, the `flow_affiliate_track` hook, retries and the delivery log.
