Back to Blog
Shoffi TeamBy Shoffi Team

Building a Partner Program for your Shopify App: a Practical Guide for You and Your Agent

It doesn't sound like too much, and honestly it's not. You generate affiliate links → partners promote your app → you pay them commissions. It is obviously getting more complex, but overall, nothing you and your agents can't figure out.

Building a Partner Program for your Shopify App: a Practical Guide for You and Your Agent

In this guide we'll go through the process of planning, building and promoting your partner program. If you are the type who likes to build first and fix as you go (no judgment), don't skip part 1. After you build your plan rules, you can go directly and copy the prompt we made for your AI agent.

Part 1: Plan The Plan

This part requires most of your human attention. Your terms should reflect your long-term affiliate partner strategy. Would you like them to make $ quickly or prove themselves first? How much can you afford to give? Can anyone just start promoting your app?

Build it by answering:

  • Who can join? Do new affiliates need approval?
  • Are all affiliates and referrals equal, or do some of them have different commissions?
  • What commission do they get? One-time, recurring, or both?
  • How long does a commission last? Lifetime or X months?
  • Is the commission based on the net or gross amount of the payment?
  • How long does the link attribution window last?
  • Do you allow manual referral submissions if affiliates do not want to use links?
  • When do commissions become approved?
  • What happens with refunds, chargebacks, and cancellations?
  • What is the minimum amount an affiliate needs to earn before a payout?
  • Should partners send an invoice before they get a payout?
  • When do you pay partners?

You can add more of course. Some apps have special rules like increased commissions based on performance, or accepting only affiliates who can be paid in a certain way. Write it all.

Part 2: Burn Tokens

Feel free to skip reading all this part and copy the prompt directly to your agent. It has all the information mentioned here in an LLM-friendly language.

This is the technical build, but the goal here is not to write code first.

The goal is to understand the system structure clearly enough that you, your developer, or your AI agent can build it without guessing.

This part assumes you already made the Part 1 plan. That plan is the source of truth.

Every technical choice below should follow those terms: who can join, whether approval is needed, how commissions work, how long attribution lasts, when commissions are approved, how refunds are handled, and when payouts happen.

The partner program has four main parts:

  1. Affiliate signup and link generation
  2. Matching Shopify shops to affiliates
  3. Pulling revenue data and calculating commissions
  4. Handling payout rules and payouts

The whole system depends on one clean chain:

Affiliate signs up
        → affiliate gets approved
        → affiliate receives a unique link/code
        → a Shopify shop installs the app through that link
        → the shop is matched to the affiliate
        → Shopify revenue is imported
        → commission is created
        → commission becomes payable
        → payout is paid
      

Do not think about payouts before attribution is reliable.

Do not think about commissions before revenue import is reliable.

The hard part is not the dashboard. The hard part is keeping the chain clean.


1. Affiliate signup and link generation

Start with the affiliate side.

You need:

  • Affiliate signup form
  • Admin approval
  • Affiliate profile
  • Affiliate code
  • Affiliate link
  • Basic affiliate dashboard

The affiliate applies first. After approval, the system creates a unique code and link.

Example structure:

Affiliate
      - name
      - email
      - website / audience
      - promotion plan
      - status
      - commission type
      - commission rate
      - commission duration
      - payout method
      - payout email
      

Affiliate statuses:

pending
      approved
      rejected
      disabled
      

Affiliate link structure:

Affiliate link
      - affiliate
      - unique code
      - destination URL
      - active / inactive
      - created date
      

Example logic:

Affiliate applies
        → admin reviews
        → admin approves
        → system creates unique affiliate code
        → system creates affiliate link
        → affiliate sees link in dashboard
      

The link can use a campaign parameter such as utm_campaign.

What matters is not the exact parameter name. What matters is that every affiliate has one unique code and that the same code can later be found in your attribution data.


2. Matching shops to affiliates

This is the attribution layer.

The goal is to answer one question:

Which affiliate referred this Shopify shop?
      

The output should be simple:

Shopify shop
        → affiliate
      

After this exists, commissions become much easier.

You do not need to solve attribution again for every payment. You only need to check whether the paying shop already belongs to an affiliate.


Where the matching data comes from

There are usually two sides:

Attribution data
      - affiliate code
      - campaign parameter
      - install event
      - timestamp
      - source payload

      Shopify / app data
      - shop domain
      - shop ID
      - app install event
      - app uninstall event
      - revenue transaction
      

The matching job combines them:

Affiliate code from attribution data
      +
      Shop domain from Shopify / app data
      =
      Matched referred shop
      

Final referred shop structure:

Referred shop
      - shop domain
      - shop ID
      - affiliate
      - affiliate link
      - attribution source
      - affiliate code
      - first seen date
      - status
      

Possible shop statuses:

active
      uninstalled
      disputed
      ignored
      

Possible attribution sources:

affiliate_code
      manual
      imported
      admin_assigned
      

Raw imported events

Do not process imported data directly.

Store raw import rows first. This makes debugging much easier later.

Raw event structure:

Imported raw event
      - source
      - external event ID
      - event type
      - shop domain
      - shop ID
      - affiliate code
      - occurred date
      - raw payload
      - processed date
      - created date
      

Sources may include:

Shopify Partner API
      BigQuery
      GA BigQuery
      CSV import
      manual import
      

Event types may include:

install
      uninstall
      attribution
      subscription created
      subscription cancelled
      

Raw import flow:

Pull external data
        → store raw event
        → normalize shop domain
        → find affiliate code
        → match affiliate link
        → create referred shop
        → mark raw event as processed
      

This gives you a safe retry path.

If matching logic is wrong, you can fix it and reprocess the raw data.


Shopify app event import

Shopify Partner data can be used to understand app events and revenue events. The source for this is Shopify's Partner API documentation. Shopify also documents app install events such as RelationshipInstalled.

At a high level, the import needs:

Shopify Partner API connection
      - organization ID
      - app ID
      - Partner API token
      - event cursor / pagination state
      - last imported timestamp
      

The app event importer should collect:

Shopify app event
      - external event ID
      - event type
      - occurred date
      - shop ID
      - shop domain
      - app ID
      - raw Shopify payload
      

Import flow:

Start from last imported event
        → pull Shopify events
        → store each event as raw data
        → move cursor forward
        → retry safely if something fails
      

Do not assume one import will always finish.

Build it so it can stop, retry, and continue without creating duplicates.


BigQuery attribution import

If your affiliate code lives in BigQuery, use BigQuery as the attribution layer. BigQuery uses GoogleSQL; keep the implementation aligned with the BigQuery query syntax docs. If the import should run automatically, use BigQuery scheduled queries or your own scheduled job.

Expected row shape:

BigQuery attribution row
      - shop domain
      - shop ID, if available
      - affiliate code
      - event type
      - occurred date
      - raw row data
      

BigQuery import flow:

Pull attribution rows
        → store raw rows
        → normalize shop domain
        → normalize affiliate code
        → match code to affiliate link
        → create referred shop if no match exists yet
      

Do not let BigQuery overwrite existing attribution automatically.

If a shop is already assigned to an affiliate, treat conflicts carefully.

Conflict handling:

Same shop, same affiliate
        → safe, ignore duplicate

      Same shop, different affiliate
        → mark as conflict
        → show in admin dashboard
        → require manual review
      

Shop domain normalization

Normalize every shop domain before matching.

Examples:

https://test-store.myshopify.com/
      TEST-STORE.myshopify.com
      test-store.myshopify.com/
      

All should become:

test-store.myshopify.com
      

This prevents duplicate shops caused by formatting differences.


Manual referrals

Manual referrals belong inside the matching step.

They are useful when an affiliate claims a referral but the affiliate code was not found.

Manual referral structure:

Manual referral request
      - affiliate
      - shop domain
      - proof / note
      - status
      - reviewed by
      - reviewed date
      - created date
      

Manual referral flow:

Affiliate submits shop domain + proof
        → admin reviews
        → admin approves or rejects
        → if approved, create referred shop
        → mark attribution source as manual
      

Do not let a manual referral automatically replace an existing attribution.

If the shop already belongs to another affiliate, send it to admin review.


3. Pulling revenue data and calculating commissions

After a shop is matched to an affiliate, revenue import becomes easier.

The question is no longer:

Who referred this payment?
      

The question becomes:

Does this paying shop already belong to an affiliate?
      

If yes, create a commission.


Revenue transaction import

Shopify revenue should be stored separately from attribution events. The relevant Shopify source is the Partner API transactions query. For app subscription revenue, Shopify's AppSubscriptionSale object includes fields such as gross amount, net amount, shop, billing interval, and creation time.

Revenue transaction structure:

Revenue transaction
      - source
      - external transaction ID
      - shop domain
      - shop ID
      - transaction type
      - gross amount
      - net amount
      - currency
      - occurred date
      - raw payload
      - created date
      

Transaction types:

charge
      refund
      adjustment
      chargeback
      

Revenue import flow:

Pull Shopify revenue transactions
        → store raw transaction data
        → normalize shop domain
        → check if shop is matched to affiliate
        → create commission if eligible
        → handle refunds or negative adjustments
      

You need to decide whether commissions are based on gross revenue or net revenue. Shopify describes netAmount as the amount added to or deducted from your payout in the AppSubscriptionSale docs.

This is a program rule, not only a technical choice.


Commission structure

Each verified revenue transaction can create one commission.

Commission structure:

Commission
      - affiliate
      - referred shop
      - revenue transaction
      - commission type
      - commission rate
      - base amount
      - commission amount
      - currency
      - status
      - reason
      - earned date
      - approved date
      - payable date
      - paid date
      

Commission statuses:

pending
      approved
      rejected
      payable
      paid
      

Commission creation rules:

Create commission only when:
      1. Revenue transaction exists.
      2. Shop is already matched to an affiliate.
      3. Affiliate is approved.
      4. No commission exists for this transaction yet.
      5. Transaction is positive revenue.
      6. Transaction is inside the commission duration.
      7. The program terms allow commission for this transaction type.
      

Commission rejection reasons:

refund
      chargeback
      affiliate disabled
      outside commission duration
      manual rejection
      duplicate transaction
      shop not matched
      

Commission approval delay

Do not approve commissions immediately if refunds are possible.

Use an approval delay.

Example flow:

Revenue transaction imported
        → commission created as pending
        → wait approval delay
        → check for refund / cancellation
        → approve commission
        → include in payout when minimum is reached
      

This protects you from paying commissions on revenue that later disappears.


Refund and cancellation handling

Refunds should not be handled as a UI detail.

They affect the money chain.

Refund flow:

Refund transaction imported
        → find original referred shop / transaction
        → find related commission
        → if commission is pending, reject it
        → if commission is approved but unpaid, reverse or reject it
        → if commission is already paid, create negative adjustment
      

Cancellation flow:

Shop cancels subscription
        → update referred shop status if needed
        → stop future recurring commissions if terms require it
        → keep historical commissions unchanged
      

The system should never silently delete financial history.

Use statuses and adjustment records instead.


4. Payout rules and payouts

Commission is not the same as payout.

A commission means money was earned.

A payout means money was actually paid to the affiliate.

You need rules before creating payouts:

Who can join?
      Do affiliates need approval?
      Are all affiliates on the same commission?
      What commission do they get?
      Is it one-time or recurring?
      How long does the commission last?
      Is the commission based on net or gross revenue?
      When does commission become approved?
      What happens with refunds and cancellations?
      What is the minimum payout?
      Is an invoice required before payout?
      When do partners get paid?
      Are manual referrals allowed?
      

Payout structure

Payout structure:

Payout
      - affiliate
      - amount
      - currency
      - status
      - payout method
      - payout reference
      - invoice required
      - invoice received
      - created date
      - paid date
      

Payout statuses:

draft
      ready
      paid
      cancelled
      

Payout-to-commission link:

Payout
        → includes commission 1
        → includes commission 2
        → includes commission 3
      

This link matters because one payout usually covers many commissions.


Payout flow

Find approved unpaid commissions
        → check minimum payout amount
        → create payout draft
        → request invoice if needed
        → admin pays externally
        → admin marks payout as paid
        → included commissions become paid
      

Minimum payout rule:

Approved unpaid commission total
        → if below minimum: do nothing
        → if above minimum: payout can be created
      

Invoice rule:

Invoice required?
        → no: payout can be marked ready
        → yes: wait until invoice is received
      

Payout payment rule:

Admin pays affiliate externally
        → save payout reference
        → mark payout as paid
        → mark linked commissions as paid
      

5. Admin dashboard

Admin needs to manage the full flow.

Do not build only a nice dashboard. Build a dashboard that helps debug the money chain.

Admin dashboard structure:

Admin dashboard

      Affiliates
      - approve
      - reject
      - disable
      - edit commission rate
      - edit commission duration

      Referred shops
      - view shop attribution
      - see affiliate code
      - manually assign shop
      - mark disputed

      Manual referrals
      - review
      - approve
      - reject

      Revenue transactions
      - view imported transactions
      - see source payload
      - debug missing commissions

      Commissions
      - view pending commissions
      - approve commission
      - reject commission
      - see source transaction

      Payouts
      - create payout
      - check invoice status
      - mark payout paid

      Program settings
      - commission rules
      - approval delay
      - minimum payout
      - invoice requirement
      - manual referral policy
      

The most useful admin pages are usually the boring ones:

Unprocessed imports
      Conflicting shop matches
      Transactions without commissions
      Commissions waiting approval
      Payouts waiting invoice
      Disabled affiliates with active shops
      

Those pages prevent support and payment problems.


6. Affiliate dashboard

The affiliate should see only what they need.

Affiliate dashboard structure:

Affiliate dashboard

      Main card
      - affiliate status
      - affiliate link
      - copy link button
      - program terms

      Performance
      - referred shops
      - pending commission
      - approved commission
      - paid commission

      Referrals
      - shop domain
      - attribution source
      - first seen date
      - status

      Commissions
      - shop
      - amount
      - status
      - earned date
      - approved date

      Payouts
      - payout amount
      - payout status
      - paid date
      - payout reference

      Manual referral form
      - shop domain
      - proof / note
      - submit request
      

Affiliate summary structure:

Affiliate summary
      - affiliate name
      - affiliate status
      - affiliate link
      - number of referred shops
      - pending commission total
      - approved commission total
      - paid commission total
      - payout history
      

Do not expose internal raw payloads or admin-only notes to affiliates.


7. Import job

Run imports on a schedule.

Example schedule:

Every 6 hours:
      1. Pull Shopify app events.
      2. Pull BigQuery attribution rows.
      3. Store raw import rows.
      4. Normalize shop domains.
      5. Match shops to affiliate codes.
      6. Pull Shopify revenue transactions.
      7. Create eligible commissions.
      8. Reject refunded or cancelled commissions.
      9. Approve commissions after the approval delay.
      10. Flag conflicts for admin review.
      

Keep imports idempotent.

That means the same import can safely run twice without creating duplicate referrals, duplicate transactions, or duplicate commissions.

Use uniqueness rules at the system level:

One external event ID per source
      One external transaction ID per source
      One commission per revenue transaction
      One active referred-shop match per shop domain
      One affiliate code per active affiliate link
      

Import job structure:

Scheduled import job

      Inputs
      - Shopify app events
      - Shopify revenue transactions
      - BigQuery attribution rows
      - manual referral approvals

      Processing
      - store raw data
      - normalize values
      - match shops
      - calculate commissions
      - handle refunds
      - approve eligible commissions

      Outputs
      - referred shops
      - revenue transactions
      - commissions
      - admin review items
      - payout-ready balances
      

8. Minimum v1 build

Build in this order:

1. Affiliate signup
      2. Admin approval
      3. Affiliate link generation
      4. Imported attribution data
      5. Shop-to-affiliate matching
      6. Manual referral review
      7. Shopify revenue import
      8. Commission calculation
      9. Refund and cancellation handling
      10. Commission approval rules
      11. Payout tracking
      12. Affiliate dashboard
      13. Admin dashboard
      

Do not start with the dashboard.

Start with the data chain:

Affiliate code
        → matched shop
        → verified revenue
        → commission
        → approved payout
      

Once that chain is reliable, the rest is mostly UI.


9. Prompt for AI agent

Use this when asking an AI coding agent to build the system. We recommend starting with Plan mode and then loop it before Claude has another outage.

Before starting, paste your Part 1 plan into the prompt. That plan should control the build. The agent should not invent program terms that were already decided there.

This prompt is intentionally detailed. It is not only asking the agent to create tables or screens. It tells the agent where the data comes from, how the data should move through the system, and how the Part 1 plan should control each rule.

You are building a partner / affiliate program system for a Shopify app.

      First, read the Part 1 program plan below. Treat it as the source of truth.

      Part 1 program plan:
      [PASTE THE PLAN HERE]

      Do not invent business rules that conflict with the plan.
      Do not use generic affiliate-program defaults when the plan gives a clear rule.
      If the plan is missing a rule that affects the build, flag it clearly before implementing that part.

      The plan may define:
      - who can join
      - whether affiliates need approval
      - whether all affiliates have the same terms
      - whether some affiliates or referrals have custom commissions
      - whether commissions are one-time, recurring, or both
      - how long commissions last
      - whether commission is calculated from gross or net payment amount
      - link attribution window
      - whether manual referrals are allowed
      - when commissions become approved
      - how refunds, chargebacks, and cancellations are handled
      - minimum payout amount
      - whether an invoice is required before payout
      - payout schedule

      Build the system around this money chain:

      Affiliate applies
        → affiliate is approved, if approval is required
        → affiliate receives a unique link/code
        → Shopify shop installs or is attributed through that code
        → shop is matched to the affiliate
        → Shopify revenue is imported
        → commission is created according to the plan
        → commission becomes approved according to the plan
        → payout becomes available according to the plan
        → admin pays externally
        → admin marks payout as paid

      Do not start with dashboard design.
      Start with the data chain.

      Main goal:
      Create a reliable system that can answer:

      1. Which affiliate referred this Shopify shop?
      2. Which Shopify payments came from that shop?
      3. Is the affiliate eligible for commission on that payment?
      4. Is the commission pending, approved, payable, paid, rejected, or reversed?
      5. Is the affiliate ready for payout under the Part 1 rules?

      Use these documentation sources when implementing Shopify and BigQuery-related imports:

      Shopify Partner API:
      https://shopify.dev/docs/api/partner/latest

      Shopify app install event object:
      https://shopify.dev/docs/api/partner/latest/objects/RelationshipInstalled

      Shopify Partner API transactions query:
      https://shopify.dev/docs/api/partner/latest/queries/transactions

      Shopify app subscription sale object:
      https://shopify.dev/docs/api/partner/latest/objects/AppSubscriptionSale

      BigQuery GoogleSQL query syntax:
      https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax

      BigQuery scheduled queries:
      https://cloud.google.com/bigquery/docs/scheduling-queries

      Do not put raw SQL, GraphQL, or provider-specific code in the first implementation plan.
      First design the objects, flows, jobs, and edge cases.
      Only write code after the structure is clear.

      --------------------------------------------------
      1. Core entities
      --------------------------------------------------

      Create the data model around these entities.
      Adapt fields to the existing app stack, but keep the meaning.

      Affiliate:
      - name
      - email
      - website / audience
      - promotion plan
      - status
      - affiliate type / tier, if the Part 1 plan has different partner groups
      - default commission type
      - default commission rate
      - default commission duration
      - payout method
      - payout email
      - invoice details, if invoices are required
      - created date
      - approved date
      - disabled date

      Affiliate statuses:
      - pending
      - approved
      - rejected
      - disabled

      Status rules:
      - If the Part 1 plan requires approval, new affiliates start as pending.
      - If the Part 1 plan allows open signup, new affiliates can become approved automatically.
      - Disabled affiliates should not earn future commissions unless the Part 1 plan says otherwise.
      - Do not delete affiliates with historical commissions.

      Affiliate link:
      - affiliate
      - unique code
      - destination URL
      - active / inactive
      - created date
      - disabled date

      Affiliate link rules:
      - Each active affiliate should have at least one unique code.
      - The code must be stable and easy to find later in attribution data.
      - The code can be stored in a campaign parameter, UTM parameter, referral parameter, or another app-defined field.
      - The exact parameter name can follow the existing app setup.
      - The system must be able to connect the same code back to the affiliate.

      Referred shop:
      - shop domain
      - shop ID, if available
      - affiliate
      - affiliate link
      - attribution source
      - affiliate code
      - first seen date
      - attribution date
      - status
      - conflict status
      - created date

      Referred shop statuses:
      - active
      - uninstalled
      - disputed
      - ignored

      Attribution sources:
      - affiliate_code
      - manual
      - imported
      - admin_assigned

      Important referred-shop rules:
      - One Shopify shop should not silently belong to two affiliates.
      - If the same shop appears again with the same affiliate, treat it as duplicate/safe.
      - If the same shop appears with a different affiliate, flag a conflict for admin review.
      - Do not overwrite an existing attribution automatically unless the Part 1 plan explicitly allows it.

      Imported raw event:
      - source
      - external event ID
      - event type
      - shop domain
      - shop ID
      - affiliate code
      - occurred date
      - raw payload
      - processed date
      - processing status
      - error message
      - created date

      Raw event sources may include:
      - Shopify Partner API
      - BigQuery
      - GA BigQuery
      - CSV import
      - manual import

      Raw event types may include:
      - install
      - uninstall
      - attribution
      - subscription created
      - subscription cancelled
      - unknown / unsupported

      Revenue transaction:
      - source
      - external transaction ID
      - shop domain
      - shop ID
      - transaction type
      - gross amount
      - net amount
      - currency
      - billing interval, if available
      - occurred date
      - raw payload
      - processing status
      - created date

      Transaction types:
      - charge
      - refund
      - adjustment
      - chargeback

      Commission:
      - affiliate
      - referred shop
      - revenue transaction
      - commission type
      - commission rate
      - base amount
      - commission amount
      - currency
      - status
      - reason
      - earned date
      - approved date
      - payable date
      - paid date
      - reversal reference, if needed

      Commission statuses:
      - pending
      - approved
      - rejected
      - payable
      - paid
      - reversed / adjusted, if needed

      Commission rejection / adjustment reasons:
      - refund
      - chargeback
      - affiliate disabled
      - outside commission duration
      - outside attribution window
      - manual rejection
      - duplicate transaction
      - shop not matched
      - transaction not eligible under Part 1 plan

      Payout:
      - affiliate
      - amount
      - currency
      - status
      - payout method
      - payout reference
      - invoice required
      - invoice received
      - payout period
      - created date
      - ready date
      - paid date

      Payout statuses:
      - draft
      - ready
      - waiting_invoice
      - paid
      - cancelled

      Payout item:
      - payout
      - commission
      - amount
      - currency

      This link is required because one payout can include many commissions.

      --------------------------------------------------
      2. Affiliate signup and approval
      --------------------------------------------------

      Build affiliate signup first.

      Required flow:

      Affiliate submits application
        → save affiliate profile
        → set status based on Part 1 approval rule
        → if approval is required, wait for admin review
        → if approval is not required, approve automatically
        → create unique affiliate code/link when affiliate is eligible
        → show link in affiliate dashboard

      Application fields:
      - name
      - email
      - website / audience
      - promotion plan
      - payout email, if useful at signup
      - any custom fields required by the Part 1 plan

      Admin must be able to:
      - approve affiliate
      - reject affiliate
      - disable affiliate
      - edit affiliate terms if the plan allows custom terms
      - see affiliate status and historical performance

      Rules from Part 1 that must affect this section:
      - who can join
      - whether approval is required
      - whether partners have tiers or custom terms
      - whether every affiliate gets the same commission
      - whether disabled affiliates keep historical commissions

      --------------------------------------------------
      3. Affiliate link generation
      --------------------------------------------------

      Create a unique code for each affiliate.

      The affiliate link should point to the app signup/install destination and include the unique code in a way your analytics or install flow can later capture.

      Link flow:

      Affiliate approved or eligible
        → generate stable unique code
        → create destination link with that code
        → store link in affiliate_link entity
        → show link to affiliate
        → keep code active unless affiliate/link is disabled

      Important rules:
      - Do not reuse active codes between affiliates.
      - Do not change affiliate codes casually, because old links may already be public.
      - If an affiliate code changes, preserve old code history or redirects.
      - If the Part 1 plan defines an attribution window, store the first attribution date and compare it later when creating commissions.

      --------------------------------------------------
      4. Where attribution data comes from
      --------------------------------------------------

      The system needs a way to connect an affiliate code to a Shopify shop.

      Possible sources:

      A. Shopify app install / relationship events
      - Use Shopify Partner API as the source for app-related events.
      - Use RelationshipInstalled or equivalent app install event data to know when a shop installed the app.
      - Store the raw event first.
      - Extract shop domain / shop ID / event time.
      - Do not assume install events include affiliate codes unless your app or analytics pipeline stores them there.

      B. BigQuery attribution data
      - Use BigQuery if your tracking data, GA events, landing page events, or affiliate campaign parameters are stored there.
      - Query rows that connect affiliate code, shop domain, event type, and timestamp.
      - Store each returned row as raw imported data before processing.
      - Use scheduled queries or your own scheduled job if the import should run automatically.

      C. Manual referrals
      - Only build this if the Part 1 plan allows affiliates to submit manual referrals.
      - Affiliate submits shop domain and proof/note.
      - Admin reviews it.
      - Approved manual referral creates a referred shop.
      - Manual referrals must not overwrite an existing shop attribution without review.

      D. CSV/manual import
      - Optional fallback for initial migration or one-time cleanup.
      - Store rows as raw imports before processing.

      --------------------------------------------------
      5. Raw import storage
      --------------------------------------------------

      Do not process external data directly.
      Always store raw imported rows/events first.

      Raw import flow:

      Pull external data
        → store raw event/row with source and external ID
        → normalize values
        → process into internal entities
        → mark raw row as processed
        → save errors if processing fails

      Why:
      - You can debug bad attribution later.
      - You can safely retry failed rows.
      - You can reprocess if matching logic changes.
      - You avoid losing provider-specific payloads.

      Idempotency rules:
      - Use source + external event ID to avoid duplicate raw events.
      - Use source + external transaction ID to avoid duplicate revenue transactions.
      - If the provider does not give an external ID, create a stable fingerprint from source, shop, event type, amount, and occurred date.
      - Running the same import twice must not duplicate shops, transactions, commissions, or payouts.

      --------------------------------------------------
      6. Shopify app event import
      --------------------------------------------------

      Use Shopify Partner API docs as the implementation reference.
      The import should be built as a scheduled job or background job.

      Connection/settings needed:
      - organization ID
      - app ID
      - Partner API token or configured auth method
      - last imported timestamp
      - cursor / pagination state
      - retry state

      Event import flow:

      Start from last cursor or timestamp
        → pull app events from Shopify Partner API
        → store each event as raw imported event
        → extract shop domain / shop ID / event type / occurred date
        → normalize shop domain
        → update app relationship status if needed
        → move cursor forward only after safe storage
        → retry safely on failure

      Events to care about:
      - app installed / relationship installed
      - app uninstalled / relationship ended, if available
      - subscription created/cancelled, if available through events or transactions

      Important:
      - Shopify app events help you know which shop installed the app.
      - They may not be enough to know which affiliate referred the shop.
      - The affiliate code usually comes from your landing page, analytics, app install URL flow, BigQuery, or another attribution capture layer.

      --------------------------------------------------
      7. BigQuery attribution import
      --------------------------------------------------

      Use this when affiliate codes or campaign parameters are stored in BigQuery.

      Expected BigQuery attribution row shape:
      - shop domain
      - shop ID, if available
      - affiliate code
      - event type
      - occurred date
      - source/campaign fields
      - raw row data

      BigQuery import flow:

      Run attribution query
        → return rows with shop + affiliate code + timestamp
        → store each row as raw imported event
        → normalize shop domain
        → normalize affiliate code
        → find matching active affiliate link
        → check attribution window from Part 1 plan
        → create referred shop if valid and not already assigned
        → flag conflicts for admin review

      If using BigQuery scheduled queries:
      - create a scheduled query that writes attribution rows to a stable table
      - have the app import from that table
      - track the last imported row/time

      If using your own scheduled job:
      - run the query from the app/backend
      - store raw rows locally
      - process rows in batches
      - save errors and continue processing other rows

      Conflict rules:

      Same shop + same affiliate
        → duplicate/safe; do not create another referred shop

      Same shop + different affiliate
        → create conflict/admin review item
        → do not overwrite automatically

      Missing affiliate code
        → store raw row
        → mark as unprocessed or ignored with reason

      Unknown affiliate code
        → store raw row
        → flag as unmatched code

      Missing shop domain
        → store raw row
        → flag as invalid row

      --------------------------------------------------
      8. Shop domain normalization
      --------------------------------------------------

      Normalize shop domains before any match.

      Examples:

      https://test-store.myshopify.com/
      TEST-STORE.myshopify.com
      test-store.myshopify.com/

      All should become:

      test-store.myshopify.com

      Normalization rules:
      - lowercase
      - remove protocol
      - remove trailing slash
      - remove spaces
      - keep the canonical myshopify.com domain where possible
      - avoid treating the same shop as different shops because of formatting

      Use normalized shop domain for matching and uniqueness.
      Keep original raw value in raw payload for debugging.

      --------------------------------------------------
      9. Shop-to-affiliate matching
      --------------------------------------------------

      Main matching question:

      Which affiliate referred this Shopify shop?

      Matching flow:

      Take raw attribution event
        → normalize shop domain
        → normalize affiliate code
        → find active affiliate link by code
        → check affiliate status
        → check attribution window, if relevant
        → check if shop is already matched
        → create referred shop or flag conflict

      Create referred shop only when:
      - shop domain exists
      - affiliate code exists
      - affiliate link exists
      - affiliate is eligible under Part 1 plan
      - attribution is inside the allowed window, if defined
      - no conflicting shop attribution already exists

      Do not solve attribution again for every payment.
      Once shop → affiliate exists, revenue matching should use that relationship.

      --------------------------------------------------
      10. Manual referrals
      --------------------------------------------------

      Only build manual referrals if Part 1 allows them.

      Manual referral structure:
      - affiliate
      - shop domain
      - proof / note
      - status
      - reviewed by
      - reviewed date
      - created date

      Manual referral flow:

      Affiliate submits shop domain + proof
        → system normalizes shop domain
        → system checks if shop already exists
        → admin reviews request
        → admin approves or rejects
        → if approved and no conflict, create referred shop
        → if conflict exists, require admin decision

      Manual referral statuses:
      - submitted
      - approved
      - rejected
      - conflict

      Rules:
      - Manual referral should never silently replace an existing affiliate attribution.
      - Manual referrals should be visible in admin dashboard.
      - Affiliates should see only their own manual referral requests.

      --------------------------------------------------
      11. Shopify revenue import
      --------------------------------------------------

      Revenue import is separate from attribution import.
      Use Shopify Partner API transactions query as the revenue source.
      Use AppSubscriptionSale docs to understand available fields such as gross amount, net amount, shop, billing interval, and creation time.

      Revenue import flow:

      Start from last imported transaction cursor/time
        → pull Shopify Partner transactions
        → store each transaction as raw revenue data
        → extract external transaction ID
        → extract shop domain / shop ID
        → extract gross amount
        → extract net amount
        → extract currency
        → extract transaction type
        → extract occurred date
        → normalize shop domain
        → save revenue transaction
        → check whether shop is matched to an affiliate
        → create commission if eligible under Part 1 plan

      Revenue transaction types to support:
      - positive charge / sale
      - refund
      - adjustment
      - chargeback

      Important:
      - Do not mix revenue transactions with attribution events.
      - Store provider raw payload for debugging.
      - Track cursor/pagination so imports can resume.
      - Make the job idempotent.
      - Do not create two commissions for the same transaction.

      Gross vs net rule:
      - If Part 1 says commission is based on gross, use gross amount as the commission base.
      - If Part 1 says commission is based on net, use net amount as the commission base.
      - If Part 1 does not say, flag it before implementing commission calculation.

      Currency rule:
      - Preserve transaction currency.
      - Do not silently mix currencies in payout totals unless the app has a defined conversion rule.

      --------------------------------------------------
      12. Commission creation
      --------------------------------------------------

      Create commission only after revenue is imported and the paying shop is matched to an affiliate.

      Commission creation flow:

      Revenue transaction imported
        → normalize shop domain
        → find referred shop
        → find affiliate
        → check affiliate eligibility
        → check transaction eligibility
        → check commission duration
        → choose gross or net base amount
        → apply commission rate/type from Part 1 plan
        → create commission as pending, unless plan says otherwise

      Create commission only when:
      - revenue transaction exists
      - transaction is positive revenue
      - shop is matched to an affiliate
      - affiliate is approved/eligible
      - no commission already exists for this transaction
      - transaction is inside commission duration
      - transaction type is eligible under Part 1 plan
      - attribution is valid under the Part 1 attribution window

      Commission types:
      - one-time
      - recurring
      - mixed one-time + recurring

      How to apply Part 1 rules:
      - If the plan says one-time commission, create commission only for the first eligible payment.
      - If the plan says recurring commission, create commission for eligible recurring payments until the duration ends.
      - If the plan says lifetime, continue while the referred shop keeps paying and the affiliate remains eligible.
      - If the plan says X months, compare transaction date to attribution/start date.
      - If the plan says different affiliates have different rates, store rules per affiliate or tier.
      - If the plan says different referrals can have custom terms, allow override at referred-shop level.

      Base amount:
      - use gross amount if Part 1 says gross
      - use net amount if Part 1 says net
      - flag missing rule if not defined

      Commission status:
      - Usually start as pending.
      - Move to approved after approval delay/rule.
      - Move to payable when it can be included in payout.
      - Move to paid only after payout is marked paid.

      --------------------------------------------------
      13. Commission approval delay
      --------------------------------------------------

      Do not approve commissions immediately unless the Part 1 plan explicitly says so.

      Approval flow:

      Revenue transaction imported
        → commission created as pending
        → wait approval delay from Part 1 plan
        → check for refund / chargeback / cancellation rules
        → approve commission if still eligible
        → make payable when payout rules are met

      If Part 1 defines:
      - approval after X days, implement that delay
      - approval only after invoice/payment settlement, implement that rule
      - no approval delay, flag the risk but follow the plan

      The approval job should run on a schedule.

      --------------------------------------------------
      14. Refunds, chargebacks, and cancellations
      --------------------------------------------------

      Refunds and chargebacks affect money.
      They must not be treated as only UI labels.

      Refund flow:

      Refund transaction imported
        → find original shop / transaction if possible
        → find related commission
        → if commission is pending, reject it
        → if commission is approved but unpaid, reverse or reject it
        → if commission is already paid, create negative adjustment
        → keep audit history

      Chargeback flow:

      Chargeback imported
        → identify affected shop/transaction
        → apply Part 1 chargeback rule
        → reject, reverse, or adjust commission
        → keep audit history

      Cancellation flow:

      Shop cancels subscription
        → update shop status if needed
        → stop future recurring commissions if Part 1 requires it
        → do not delete historical commissions

      Important rules:
      - Never silently delete financial history.
      - Use statuses and adjustment records.
      - Affiliates should see clear statuses, not raw internal payloads.
      - Admin should be able to see the reason for rejection/reversal.

      --------------------------------------------------
      15. Payout rules
      --------------------------------------------------

      Commission is not payout.
      Commission means money was earned.
      Payout means money was paid.

      Payout creation flow:

      Find approved unpaid commissions
        → group by affiliate and currency
        → check minimum payout amount from Part 1 plan
        → check invoice requirement from Part 1 plan
        → create payout draft or waiting-invoice payout
        → admin pays externally
        → admin saves payout reference
        → admin marks payout as paid
        → linked commissions become paid

      Minimum payout:
      - If approved unpaid commission total is below the minimum, do not create a ready payout.
      - If the total reaches the minimum, payout can become ready, unless invoice is required.

      Invoice rule:
      - If invoice is not required, payout can become ready when minimum is reached.
      - If invoice is required, payout waits until invoice is received.
      - Store invoice received status.

      Payout schedule:
      - Follow the Part 1 payout schedule.
      - If the plan says monthly, generate payout candidates monthly.
      - If the plan says manual/admin-triggered, allow admin to create payout on demand.

      Payment execution:
      - The system does not need to send money automatically unless explicitly requested.
      - Admin can pay externally through PayPal, Wise, bank transfer, or another method.
      - Admin then records payout reference and marks payout as paid.

      --------------------------------------------------
      16. Admin dashboard
      --------------------------------------------------

      Build admin screens to manage and debug the whole chain.

      Admin dashboard sections:

      Affiliates:
      - list affiliates
      - approve / reject / disable
      - edit commission settings if allowed
      - view affiliate link
      - view referred shops
      - view commission and payout history

      Referred shops:
      - list shops
      - show affiliate attribution
      - show attribution source
      - show affiliate code
      - show first seen date
      - mark disputed / ignored
      - manually assign if admin policy allows

      Manual referrals:
      - show submitted requests
      - approve / reject
      - show conflicts
      - show proof/note

      Imports:
      - show raw imported events
      - show source
      - show processed/unprocessed status
      - show errors
      - allow retry/reprocess where safe

      Revenue transactions:
      - show imported transactions
      - show shop
      - show gross/net/currency
      - show whether commission was created
      - show why commission was not created

      Commissions:
      - list pending / approved / payable / paid / rejected
      - approve or reject manually if admin permissions allow
      - show source transaction
      - show rejection/reversal reason

      Payouts:
      - create payout drafts
      - show payout-ready affiliates
      - show invoice status
      - mark invoice received
      - mark payout paid
      - save payout reference

      Program settings:
      - store settings from Part 1 plan
      - approval delay
      - minimum payout
      - invoice requirement
      - manual referral policy
      - attribution window
      - default commission rule

      High-value admin debug pages:
      - unprocessed imports
      - unmatched affiliate codes
      - conflicting shop matches
      - transactions without commissions
      - commissions waiting approval
      - payouts waiting invoice
      - disabled affiliates with active shops

      Do not hide failed imports.
      Failed imports should be visible and fixable.

      --------------------------------------------------
      17. Affiliate dashboard
      --------------------------------------------------

      Affiliate dashboard should show only what the affiliate needs.

      Affiliate dashboard sections:

      Main status:
      - affiliate status
      - whether approval is still pending
      - affiliate link/code
      - copy link button
      - relevant program terms from Part 1 plan

      Performance:
      - referred shops count
      - pending commission total
      - approved commission total
      - payable commission total
      - paid commission total

      Referrals:
      - shop domain
      - attribution source
      - first seen date
      - status

      Commissions:
      - shop
      - amount
      - status
      - earned date
      - approved date
      - rejection/reversal reason when relevant

      Payouts:
      - payout amount
      - payout status
      - paid date
      - payout reference, if safe to show

      Manual referral form:
      - show only if Part 1 allows manual referrals
      - affiliate enters shop domain and proof/note
      - affiliate can see submitted manual referral status

      Do not expose:
      - raw provider payloads
      - internal admin notes
      - other affiliates' data
      - private merchant data that is not needed for the affiliate

      --------------------------------------------------
      18. Scheduled jobs
      --------------------------------------------------

      Create scheduled jobs for imports and state changes.

      Suggested recurring job:

      Every few hours:
      1. Pull Shopify app events.
      2. Pull BigQuery attribution rows, if used.
      3. Store raw import rows/events.
      4. Normalize shop domains and affiliate codes.
      5. Match shops to affiliate codes.
      6. Pull Shopify revenue transactions.
      7. Store revenue transactions.
      8. Create eligible commissions.
      9. Process refunds, chargebacks, and cancellations.
      10. Approve commissions after approval delay.
      11. Flag conflicts and failed rows for admin review.
      12. Update payout-ready balances.

      The exact schedule can depend on the app size.
      The job must be safe to retry.

      Idempotency requirements:
      - one raw event per source + external event ID
      - one revenue transaction per source + external transaction ID
      - one commission per revenue transaction
      - one active referred-shop match per normalized shop domain
      - one active affiliate code per affiliate link
      - one payout item per commission

      --------------------------------------------------
      19. Build order
      --------------------------------------------------

      Build in this order:

      1. Program settings based on Part 1 plan
      2. Affiliate signup
      3. Admin affiliate approval
      4. Affiliate link/code generation
      5. Raw import storage
      6. Shopify app event import
      7. BigQuery attribution import, if used
      8. Shop domain normalization
      9. Shop-to-affiliate matching
      10. Manual referrals, if allowed
      11. Shopify revenue transaction import
      12. Commission calculation
      13. Refund / chargeback / cancellation handling
      14. Commission approval job
      15. Payout draft and payout tracking
      16. Admin dashboard
      17. Affiliate dashboard
      18. Import/debug pages

      Do not build the dashboard first.
      First prove this chain works:

      affiliate code
        → matched shop
        → verified Shopify revenue
        → commission
        → approved payout

      --------------------------------------------------
      20. Output expected from you before implementation
      --------------------------------------------------

      Before writing code, produce:

      1. A short summary of the Part 1 rules you found.
      2. A list of missing rules that block implementation.
      3. A high-level data model.
      4. Import flow for Shopify app events.
      5. Import flow for BigQuery attribution, if relevant.
      6. Revenue import flow.
      7. Commission creation rules.
      8. Refund/chargeback/cancellation handling rules.
      9. Payout flow.
      10. Admin dashboard page list.
      11. Affiliate dashboard page list.
      12. Build order.

      After that, implement in small steps.
      At every step, keep the Part 1 plan as the source of truth.
      

Part 3: Ship It!

So a few hours/days later, and after 4-5 times reaching your token limits, you finally made it! What now?

Building the program does not mean people will join it. You need to promote it like any other channel.

A few places to start:

  • Existing users who already like your app and would like to recommend it
  • Shopify agencies
  • People already promoting other Shopify apps

You can find here more info about how to find affiliate partners.

The main challenge is not only finding them. It is making them trust the program. Do not just send them a link and expect them to be your ambassador. Give them more: product explanation, target audience, commission terms, screenshots, demo access if needed, and clear payout rules.

To be more convincing, you can feature a success story of one of your affiliate partners who is happy with the relationship. Remember that a good relationship is not necessarily just earning $, it can even be better support and custom features that are made for partners. If you need more info about how to make your partners successful, try this blog post.

Self-built vs Shoffi

You can build all of this yourself. For some apps, that is the right choice. If you want full control over the design and the data, a self-built partner program checks those boxes.

But then you own everything around it too: the affiliate support, the code maintenance (and oh boy Shopify loves changing their APIs). You will also need to find your own affiliates and build trust.

When a partner joins a private partner program, they need to trust that your tracking is correct and that you will pay them on time. Most app owners are honest, but from the partner side, it is still a private system controlled by the person who needs to pay them.

Most agencies and freelancers in the ecosystem are already using Shoffi (since 2021!). They track their performance in one place, and contact Shoffi if they need help. If there is a payment issue, Shoffi can follow up and help them. This builds trust.

If you want full control and you are ready to maintain the system and relationships with partners, build it yourself. If you want to focus on your app and not turn your partner program into another product to maintain, Shoffi is here for you. Start here for free :) If your app combines Shopify app revenue with Stripe revenue from other channels, you can try combining Shoffi with other solutions like RefKit.