Platform Fees reference
The Ignition Platform Fees add-on takes a platform commission out of each contribution that a split-capable gateway settles at capture, and keeps every creator- and admin-facing money figure consistent with what it took.
It exposes no hooks of its own in this version. It is built entirely out of core’s seams, and its own surface for other add-ons is a set of read accessors.
Dependency floors
Section titled “Dependency floors”The add-on refuses to bootstrap unless all of these hold, and shows a notice-error naming the one that failed:
| Constant | Floor | Enforced |
|---|---|---|
IGCF_FEES_REQUIRES_CORE |
0.23.0 |
Always. |
IGCF_FEES_REQUIRES_PLATFORM |
0.2.0 |
Always — Platform Mode must also be active. |
IGCF_FEES_REQUIRES_PAYPAL |
0.3.0 |
Only when igcf-paypal is active. |
The PayPal floor is asymmetric on purpose. An absent igcf-paypal passes the gate, because commission is settled by whichever gateway answers igcf_payment_attempt_settles_application_fee — requiring a specific gateway to be installed would shut out a future split-capable one. An igcf-paypal that is installed but predates that answerer can never settle, so it is gated.
When a commission is configured (any rate or any flat minimum) but no switched-on payment method can both split a pledge and report itself set up, the add-on renders a notice-warning instead: it is running, but nothing can collect what has been configured. The check reads core’s payment-method registry, not this seam — a method counts only when get_enabled_gateways() lists it, it supports( 'platform_mode' ), and is_configured() answers true — so the warning follows what will actually be collected rather than what is merely installed. It is evaluated at admin_notices, not at bootstrap, because the registry is filled on init priority 5.
The notice is additionally gated on igcf_pledges_go_to_creator(). On a platform-routed site the commission is uncollectable for a different and more specific reason, and that case is owned by the settings screen’s own notice and by the routing-consequences answer below, so the two never stack.
Core seams consumed
Section titled “Core seams consumed”| Seam | How it is used |
|---|---|
igcf_operator_settings |
Answered with an entry for the igcf_fees_settings option (group igcf_fees_settings_group) listing both stored keys, rate_bps and minimum_minor, so operators can open and save the Platform Fees settings page. A core older than 0.23.0 never applies the filter. |
igcf_payment_attempt_settles_application_fee |
Read, never answered. The single gate on both the fee contribution and the charge record, so a commission is only ever contributed and recorded where a gateway settles it. |
igcf_payment_attempt_application_fee_components |
Answered with a commission key, added only when the attempt settles a split and the commission is > 0. Keys are distinct by construction, so this callback’s priority (10) is cosmetic. Replaced the scalar igcf_payment_attempt_application_fee contribution, which the add-on no longer hooks. |
igcf_projected_charge_context |
Answered at priority 20 with components['commission'], computed from $context['pledge_amount'] — the attempt-free sibling of the components seam, so checkout can price a commission before any Igcf_Payment_Attempt row exists. Priority is load-bearing: a gateway answers settles_application_fee first (igcf-paypal at 10) and this reads that answer before deciding whether to project anything. |
igcf_pledge_net_amounts |
Answered with Σcharge − Σreversal per pledge, which is what makes every creator- and admin-facing net figure commission-aware at once. Subtracted without a floor, so the net may finish below zero. |
igcf_payment_attempt_net_to_creator |
Answered per attempt, for consumers reading the attempt-level figure. Also unfloored — core’s get_net_to_creator() stopped clamping in 0.22.0. |
igcf_pledge_settlement_terms |
Declares the Commission term on the wp-admin pledge money statement — one per succeeded payment attempt, destination platform, order 300, valued at what the add-on recorded less what it reversed, and declared even at zero whenever a charge row exists, so a reversed commission reads $0.00 rather than vanishing. Replaced igcf_pledge_detail_money_rows, removed in core 0.22.0. |
| The four attempt/refund column seams | The per-row Commission column on the Payment Attempts table and the per-row Commission reversed column on the Refunds table. Unchanged by the 0.22.0 removal. |
igcf_pledge_routing_consequences |
Answered with a Platform Commission entry when the site currently routes pledges to creators and a commission is configured, so core’s Who Receives Pledges screen can say what switching to central collection would turn off. Answered through igcf_pledge_routing_consequence() rather than by appending directly, so a malformed answer cannot break the next add-on’s append. |
igcf_platform_creator_stat_tiles |
The Platform Commission and Net after Commission tiles on a creator’s wp-admin detail view. |
igcf_creator_pledge_export_columns / igcf_creator_pledge_export_row |
The Commission and Net after Commission columns on a creator’s pledge export. |
Commission is deliberately kept off backer-facing surfaces. Core renders those through Igcf_Pledge_Service::get_pledge_refund_amounts(), which does not apply the net seam — see the Service Layer.
Calculation
Section titled “Calculation”commission = max( round( pledge.amount × rate_bps ÷ 10000 ), minimum_minor )if ( commission >= pledge.amount ) commission = 0Computed on pledge.amount — the base contribution — never on attempt.amount, which a contributions add-on may inflate through igcf_pledge_amount_to_charge. Settings are read from Igcf_Fees_Settings::get() under the igcf_fees_settings option, as rate_bps (basis points) and minimum_minor (minor units).
The second line is the creator-protection cap, and it compares against the pledge, never the charged amount: a voluntary tip or cover added on top of a pledge must not raise the total enough to make a commission collectable that would otherwise have taken the creator’s whole share. The base is clamped at 0 first, so a negative amount cannot invert the floor. All three consumers treat amount === 0 as “no commission”.
| Method | Returns |
|---|---|
Igcf_Fees_Calculator::breakdown_for_amount( $pledge_amount_minor ) |
The amount alongside the terms that produced it — base_minor, rate, minimum_minor — from a bare pledge amount, for callers with no pledge row (the projected-charge answer, and the settings-screen warning). All values >= 0. |
Igcf_Fees_Calculator::breakdown_for( Igcf_Pledge $pledge ) |
The same breakdown for a pledge object. Delegates to breakdown_for_amount(), so the cap applies identically. |
Igcf_Fees_Calculator::commission_for( Igcf_Pledge $pledge ) |
Just the amount. |
The drop condition
Section titled “The drop condition”A split gateway settles a platform fee only when the whole application fee is strictly below the charge amount; at or above it, igcf-paypal’s split_order_args() drops the fee to 0 and creates the order without it. record_charge() mirrors that condition against the same quantity the gateway reads — Igcf_Payment_Attempt_Service::get_application_fee() versus the attempt amount — and records no charge row when it holds, so the books cannot show a cut the gateway never took.
This guard is now the gateway mirror only. Keeping the creator whole is the calculator’s cap above, which has already zeroed an over-the-pledge commission before an attempt reaches here; in ordinary operation a commission below the pledge is always below the charge, so the guard is unreachable. It remains because the comparison is on the total application fee, not on the commission alone: another add-on contributing to igcf_payment_attempt_application_fee_components can still push the total above the charge.
Read accessors
Section titled “Read accessors”Public static methods other add-ons can read. All amounts are in minor units.
| Method | Returns |
|---|---|
Igcf_Fees_Plugin::commission_label() |
The Title Case name this add-on gives the commission wherever it is shown — currently Platform Commission. Read it rather than re-translating the words, so an operator sent to a named thing finds it called exactly that. |
Igcf_Fees_Plugin::commission_is_configured( $rate_bps, $minimum_minor ) |
Pure predicate: is a commission configured at all? True when either field is set — a 0% rate with a flat minimum still collects. The single reader; the admin notice and the settings screen both delegate to it. |
Igcf_Fees_Plugin::a_gateway_can_settle() |
Impure: does core’s payment-method registry currently hold a switched-on, set-up, split-capable method? |
Igcf_Fees_Charge::amount_for_attempt( $attempt_id ) |
Commission charged against one payment attempt. |
Igcf_Fees_Charge::get_for_attempt( $attempt_id ) |
The full charge row, including the rate and floor in force when it was written. |
Igcf_Fees_Reversal::amount_for_refund( $refund_id ) |
Commission reversed against one refund. |
Igcf_Fees_Net::commission_by_pledge( array $pledge_ids ) |
Net commission (Σcharge − Σreversal) keyed by pledge id. |
Igcf_Fees_Net::net_commission_for_attempt( $attempt_id ) |
Net commission for a single attempt. |
Igcf_Fees_Net::charge_by_attempt_for_pledge( $pledge_id ) |
Recorded charges keyed by attempt id, for the pledge’s succeeded attempts. Presence in this map — not a non-zero amount — is what decides whether a commission term is declared at all. |
Igcf_Fees_Net::commission_by_campaign( $campaign_id ) |
Net commission across a campaign. |
Igcf_Fees_Net::commission_for_creator( $user_id, $scope ) |
Net commission across everything a creator has raised. $scope is required — this reads across campaigns, so without one it would mix live and test money. See query scope. |
Igcf_Fees_Net::gross_terms_by_pledge( array $pledge_ids ) |
Gross and refunded sums keyed by pledge, for building a net figure alongside the commission. Already narrowed to specific pledges, so it takes no scope. |
Igcf_Fees_Net::gross_terms_by_campaigns( array $campaign_ids, $scope = 'unscoped' ) |
The same sums scoped by campaign instead of by pledge id. Pass a $scope to count one side only; it is applied to the charge and reversal halves together, so a scoped charge can never net against an unscoped reversal. Throws InvalidArgumentException on a value that is not live, test or unscoped. |
Storage
Section titled “Storage”Two custom tables, both dropped on uninstall along with every igcf_fees_% option:
| Table | Row per | Holds |
|---|---|---|
{prefix}igcf_fees_charge |
Payment attempt | The commission charged, plus the base, rate and floor it was computed from. |
{prefix}igcf_fees_reversal |
Refund | The proportional commission reversed. |
Charge rows are written idempotently on attempt creation, and only for attempts that pass the settlement gate. They are an audit record: they store the terms that produced the amount, not just the amount, so a row can be explained later even after the settings change.