Documentation for larsasub v0.38.2.
On an older version? Your release notes ship with the package (CHANGELOG.md).
Compliance: what LarsaSub does, and what stays yours
LarsaSub automates subscription billing; it does not make you compliant. This page lists the obligations a consuming application inherits, and for each one what the package handles and what you must arrange yourself. Read it before you go live, and again when you change how amounts, mandates, notices or documents work. Items marked yours have no package support today.
- SEPA Direct Debit
- VAT & invoicing
- Selling to consumers
- Privacy (GDPR)
- Payouts and third-party funds
- Connect marketplaces
SEPA Direct Debit
Every recurring collection runs on a mandate, and SEPA requires the debtor to be told the amount and date before a collection. The package sends the notice for a fixed series and for changed amounts; the creditor role, your mandate terms and any per-collection promise stay yours.
Pre-notification
The package provides: a one-time pre-notification for a fixed recurring series. The activation email states the amount to be debited — the plan price plus any cost lines active on the first billing day, VAT-converted exactly as the charge will be, and labelled as such — the introduction price and how long it lasts, the price after it, the start date and the next payment date. That satisfies the rulebook's notice requirement for a series whose amount does not change. The amount of a series does change when a cost line or the VAT rate moves it; those changes are announced as below.
The debited amount is what the notice must state, and it is not always the
listed price: on a VAT-exclusive install the collection is the listed price
plus VAT, and during an introduction period the charge bills the introduction
price. The activation, started, renewal-announcement, renewed, plan-change and
VAT-rate-change mails all resolve their amount through
Support\MonthlyAmountSummary, so they cannot state different figures for the
same subscription. The renewal
announcement is a pre-notification in its own right: it announces a future
collection on its own scheduled path.
When the notice reaches the customer depends on how the subscription started:
| Flow | First collection from the mandate |
|---|---|
Checkout (createSubscription()) |
The customer pays the first period at the checkout; the activation mail follows, and the first collection from the mandate is the next period's — a billing period or more after the notice |
Deferred start (createDeferredMandateSubscription()) |
Activated, and mailed, right away; the planner collects from start_date |
Mandate reuse (createSubscriptionWithMandate()) |
The first collection is submitted at once, and the activation mail goes out when it is paid — after the debit, not before it |
Yours:
- Creditor Identifier and mandate terms. You are the creditor. You need a Creditor ID from your bank and mandate terms that state your pre-notification period; the package stores neither.
- Per-collection notice. The package does not announce each subscription
collection: the notice above covers the series, and a subscription charge is
only created on its collection day, so
notify_charges_days_beforenever reaches it. If your mandate terms promise a notice before every collection, send it yourself: the date fromgetNextBillingDate()(null: the planner plans no next charge as things stand — charges that already exist, such as a standalone one, are not in that answer, so check those separately), the amount fromSubscriptionCostService::calculateChargeAmount()for that date, less the available credit — the usual per-period figure leaves out a cost line's catch-up (see UPGRADE). - Mandate reuse.
createSubscriptionWithMandate()collects against an existing mandate before any mail for this subscription has gone out. That is fine for an unchanged series under terms that allow it; otherwise announce the first amount yourself before you call it. - Ad-hoc standalone charges (usage, tokens, per-collection amounts) are
announced per collection, which is what the amount-varies case needs. Plan
them with
RecurringChargeService::createStandaloneCharge(): it setsnotifyby default and freezes the VAT decision at creation. The lead time is whateverplanned_onallows: a charge is only announced while its day is still ahead, and on the shipped schedulelarsasub:execute-chargesruns beforelarsasub:notify-chargesin the same minute. To give N days' notice, plan the charge at least N + 1 days ahead (see Notify Charges Command).
Changed amounts
The package provides: when the amount changes through a cost line (add,
rate change, end), an email with the new amount per period
(notifications.user.subscription_cost_changed, on by default) — once per
change act, listing everything that moved. The mail goes out when the change
is made, and the next charge collects the new amount on its collection day,
so a change made shortly before a collection day is announced shortly before
the debit.
One category is deliberately not mailed as a change: the subscription's first set of cost lines, on a subscription that has never been charged and whose lines take effect on or before its start date. That is the amount the activation notice itself states, so it is covered by that notice rather than by a correction to it. Everything after that is announced. See Cost lines for the exact rule and the one residual gap (a subscription that starts with no cost lines and gains its first before its first charge).
When the VAT rate of a plan changes through the model, the subscribers
whose debit it moves (exclusive prices for a customer who pays VAT, or a
zero-rated customer on inclusive prices) get an email with the new amount per
period
(notifications.user.subscription_vat_rate_changed, on by default), running
and upcoming subscriptions alike. There is no lead time: the next charge is
composed at the new rate. See VAT rate changes.
Yours: a change of the plan price (as opposed to a cost line) is a host
decision: announce it, and offer termination where consumer law requires. So is
the lead time before a VAT rate change, and the announcement of a change of
invoicing.vat_rate, which the package cannot observe.
Chargebacks
The package provides: with dunning.chargeback_follow_up on (the
default), a SEPA chargeback after settlement becomes a
follow-up charge the customer pays
themselves, through a checkout. The package never re-collects it from the
mandate: that would need a fresh pre-notification, and after an MD01
("no valid mandate") it may not be collected from that mandate at all. The
credit note for the charged-back invoice is issued as before; the follow-up is
invoiced when it is paid.
When the bank reverses the chargeback, an unpaid follow-up is withdrawn. A
paid one means the customer paid the period twice, and dunning.double_paid
decides what happens: signal (the default) flags it so the operator is told
and gives it back; refund refunds it, normally on the original payment, so
the follow-up's invoice stays the customer's document; credit returns it as
wallet credit. A reversal that meets no paid follow-up leaves the chargeback's
credit note standing while the money is back: the package has no debit note,
so your bookkeeping corrects it.
Yours: the bank's chargeback fee (Mollie charges one per SEPA chargeback) and any collection costs or interest are not passed on by the package; whether and how you charge them is a decision under your own terms.
Reminders
The package provides: the reminder series
(off by default), which reminds and nothing more. Its mails state the amount
open and how to pay it; they name no consequences, costs or deadlines, and an
escalate step is an event to your application — not a notice of default. The
package never suspends access, never re-collects a failed charge on its own,
and never adds costs.
Yours:
- Escalation. Whether a debt is escalated, access suspended or collection costs charged is yours, under your terms and the law. Charging collection costs to a consumer in the Netherlands requires the statutory fourteen-day notice first (Art. 6:96 lid 6 BW, WIK), which the package does not send.
- The wording of the payment-failed mails. The payment-failed, -canceled
and -expired mails to the customer say only that the amount remains open
until it has been paid
(
larsasub::notifications.user.payment-failed.warningand its two siblings). If you do suspend access or escalate, and want the mail to say so, override that sentence in your published translations.
VAT & invoicing
With invoicing on, the package decides the VAT of every charge and issues the
invoices and credit notes. Invoicing ships off; turning it on with an empty
seller VAT number logs a warning and larsasub:doctor flags it.
The package provides:
- NL/EU rules for the common cases — the domestic rate from
invoicing.vat_rateor a per-planvat_rate, resolved for each subscription charge when it is composed (VAT rate changes); the reverse charge for EU B2B (a customer in another member state than the seller, with abusinessprofile carrying a VAT number — both required — and, with the opt-in VIES check on, a number VIES confirmed); and zero-rating outside the EU. - Collected always equals invoiced (below).
- Per-refund credit notes — a partial refund credits only the refunded amount.
- Gapless, continuous invoice numbering, per tenant under Connect.
- Invoices and credit notes that are enforced immutable — corrections happen through credit notes, and deletion is refused.
- No invoice for the €0,01 mandate verification — it verifies a payment method and is not a delivery, so no invoice number is consumed for it. How you book the cent is yours.
With invoicing off the package converts no amount: what is listed is what is debited.
Collected equals invoiced
The VAT the invoice states is always contained in the value that was settled;
the package never invoices more than it collected, in both price modes. Where
credit was applied, the invoice states the full service value and
total - credit_applied is what the customer was debited — the credit is part
of the settlement, not a discount on the taxable amount.
prices_include_vat = true: the listed price is gross, and a zero-rated customer is charged the NET amount (the VAT they do not owe is not collected).prices_include_vat = false: the listed price is net, so the VAT is ADDED before collection — a €14,90 plan debits €18,03 domestically. Zero-rated customers are charged the listed €14,90.- A customer without a country is neither stripped nor grossed up (the package does not guess in either direction); the invoice then splits the collected amount into net + VAT, which is the conservative reading — nothing extra was collected.
- The rate and the zero-rating that applied are frozen on the charge, so an invoice generated later (repair command, on-demand download) reports the VAT that was actually collected, even if the plan rate or the customer's profile changed in between.
VAT numbers checked in VIES
The package provides: an opt-in check of business VAT numbers in VIES
(invoicing.vat_validation, see
Validating VAT numbers). On, the reverse
charge applies only to a number VIES confirmed, for that very number; until
then the business customer pays VAT and reclaims it, so no VAT is lost on a
number nobody verified. The check is repeated (by default every 30 days). The
consultation number VIES issues to you as requester is frozen with the sale and
kept on the reverse-charged invoice — also after erasure — and a charge keeps
the decision of the day it was created.
In every mode the profile and the document agree: the reverse charge requires
invoice_type = 'business' — a stored number alone grants nothing — and a
profile that is not business has its company name, VAT number and CoC number
cleared on save, whatever writes it. A query-builder mass update dispatches no
model events and skips that clearing; the type requirement still holds, so no
zero rate follows from it, and a VIES status only counts for the number it was
decided for.
Yours: with the check off, the reverse charge is applied on the number the customer typed. Validate it and keep the confirmation, or the VAT is yours to pay — or switch the check on (backfill first, see Validating VAT numbers). Either way, comparing the customer's name and address, which art. 18(1)(a) of Regulation 282/2011 also asks for, stays yours: the package does not read or store what VIES returns for them. A VIES confirmation does not by itself establish the right to the exemption.
VAT rate changes
The package provides: a subscription charge takes the VAT rate in force
when it is composed, on its collection day: the subscription's own override,
else the plan's rate, else invoicing.vat_rate. A subscription never keeps the
rate it was signed up at, so a legal change, or a corrected rate on a plan,
reaches every running subscriber from their next charge (Directive 2006/112/EC,
art. 65 and 93: for a payment on account, the rate on receipt). Paid charges and
their invoices keep the rate they were collected at. When a plan's rate changes
through the model, the subscribers whose debit changes are mailed the new
amount (notifications.user.subscription_vat_rate_changed); see
Changed amounts.
Standalone charges and single payments fix their VAT when they are created.
Yours:
- The transition days. Composition is not receipt: a SEPA debit is
received days after it is composed, a charge paid by hand later keeps the
rate it was composed at, and the invoice is dated at the payment. Make a rate
change take effect between the last planning run before its date and the
first one on it (both at midnight in
scheduling.timezone), and handle the charges around it under your member state's transition rules. - Changes the package does not observe mail nobody:
invoicing.vat_ratein the configuration, a rate written onto a plan with a query-builder update, and an override set on a single subscription. Each can move a debit. Announce those yourself, or set the rate on the plan through the model so the mail goes out.
Your VAT obligations
- Cross-border B2C (OSS). Past €10,000/year of cross-border B2C supplies the customer's country's rate applies. The package charges your configured rate and does not track the threshold; you need an OSS registration and a per-country rate source.
- Non-EU customers are zero-rated without an exemption note on the invoice and without a business check; you may still owe UK, Norwegian or Swiss VAT.
- A seller in a member state without the euro (Poland, Sweden, ...) invoices its subscriptions in euros here: an invoice is always in the currency its payment was collected in. The VAT amount must then also be stated in the national currency of the member state where the supply takes place — for a domestic sale, yours (Art. 230 of the VAT Directive). The invoice does not do that; see Currency.
- Retention: keep invoices 7 years (NL; 7–10 elsewhere). The package never deletes them — do not add a cleanup job for them.
Selling to consumers
Consumer law adds rules on notice periods, on what the order button says, and on the right of withdrawal. The bundled dashboards carry the package's part of each; a headless host owns its own screens and with them these duties.
Notice periods
The package provides: a default cap on notice periods. Plans a tenant
defines through the bundled dashboard are refused with a notice period longer
than subscription.max_notice_period_months (default 1 — NL BW 6:236 sub j
voids a longer period after silent renewal). A cancellation inside the notice
window genuinely extends the contract and bills the extra months, which is why
the cap is a money setting, not cosmetics.
The cap is one package-wide number, and only a validation. It applies to
both notice-period fields and cannot vary per tenant, per plan or per customer
type. Raising it for B2B therefore also removes the consumer-safe default for
every B2C subscriber on the installation, and disabling it (null) removes it
everywhere. It is checked in the bundled tenant plan form only: plans you
create in code or an admin panel are not capped, and existing plans with a
longer period keep it.
Yours: a platform that sells to both audiences needs its own per-tenant or per-plan policy on top — enforce it in your own admin flow before you raise this value.
The order button
The package provides: an order button on the bundled subscription
dashboard that names what the click collects (NL BW 6:230v, EU Consumer Rights
Directive art. 8). Each plan card prints the quote of its first charge — VAT
added or stripped as the charge will, the lines a before_first_charge hook
adds, an introduction price and what follows it — with the VAT label the
subscription mails use, and repeats the amount directly above the button. On a
paid plan the button reads "Order with obligation to pay" / "Bestelling met
betalingsverplichting", the statutory formula; a free plan keeps "Get started".
Where no amount can be named there is no button: the card says why (enter a country first on exclusive prices, add a payment method first, or a hook that cannot state its lines), and the server refuses the same subscribe — before any terms evidence or registration exists. The texts are built into the bundle; a host that rebuilds the components with its own wording owns that wording.
Yours: the amount on the button, in your own checkout. A headless flow, or
a host that replaced the bundled subscription dashboard, owns its confirm step
and has to name the amount that will be charged there. Four things can put the
debit somewhere other than the catalogue price: an introduction price, cost
lines added by a before_first_charge hook, exclusive prices
(invoicing.prices_include_vat set to false: the charge grosses up for every
customer the country rules do not zero-rate), and — with invoicing on and the
default inclusive prices — a zero-rated customer (reverse charge, outside the
EU), whose charge has the VAT taken off.
The package gives you the amount, not the screen:
SubscriptionCostService::quoteFirstCharge() returns it from the charge's own
calculation, which is the only place it can come from — VAT is converted per
breakdown row and re-summed, so a host that grosses up its own total can be a
cent or two off. Print its rows and its total, and see
Cost lines for the three flags that
say the total is not the whole story — one of which, creationRefused, says
the subscription cannot be created at all, so there is no order button to
render rather than one with a different number on it. On installs with
invoicing.prices_include_vat = false the quote refuses to answer without
a customer country rather than name an amount short by the full VAT rate.
Terms and the right of withdrawal
The package provides: with a terms provider configured (terms.provider,
none by default), the bundled subscribe flow records each acceptance — the
version, the language it was shown in, the time, and a keyed one-way hash of
IP address and user agent, never the raw values — before the payment starts.
With terms.withdrawal_consent on, it also requires and records a second,
separate consent: to immediate performance, and acknowledgement of losing the
14-day withdrawal right. See Terms acceptance.
Yours:
- Consent in your own flow. A headless host records acceptance itself (see Terms acceptance); without a terms provider nothing is recorded at all.
- The right of withdrawal. For a 14-day distance contract you must obtain the consents above, confirm both on a durable medium, and refund pro-rata on withdrawal. The package's refund and credit paths can execute that refund; deciding the amount is yours.
Privacy (GDPR)
The package gives you an erasure path, retention settings for what it logs, and a record of what each table holds. The controller's duties — securing the database, your privacy notice, erasure at Mollie — stay yours.
Erasure
The package provides: Billable::anonymize(), the erasure path. It
refuses (with a LarsaSubException) while the registration has an active or
upcoming subscription — cancel or end it first. Otherwise, in one transaction,
it:
- clears every personal field on the registration — name, email, company, VAT
and CoC number, address, country, locale — and stamps
anonymized_at; the row survives for FK integrity; - removes the registration's own model logs and notification-delivery records;
- clears the VIES check of the VAT number (status, checked number, consultation number, date);
- clears the frozen
vat_customersnapshot on every charge and single payment, soft-deleted ones included; - strips the personal keys out of the provider's own detail bags — payer name,
IBAN, BIC, billing and shipping address, card holder, the last four card
digits, the card fingerprint and the PayPal payer id — on payments and
mandates alike, again including soft-deleted rows. The mandate's
consumer_accountcolumn holds the account identifier (the IBAN, a card's last four digits or the PayPal payer id) and is emptied outright.
Invoices and credit notes keep their frozen customer snapshot, under the Art. 17(3)(b) legal-obligation exception. The detail bags are stripped rather than emptied: the rest of them (payment id, status, amounts, timestamps, settlement) is how a payment stays reconcilable long after the person is gone, and erasure has no reason to cost that.
Two things erasure does not touch:
metadata. That key carries whatever your app put on the payment, and the package cannot tell an order number from a name. If you store personal data there, erase it yourself — the models are plain Eloquent andanonymize()is safe to call your own code around.- Terms acceptances. They are append-only evidence of what the
registration accepted, and hold no name and no raw IP address or user agent
— only the keyed hash. They go only with the registration itself, through
larsasub:prune-billables --orphaned(below).
Orphaned registrations are deleted outright, terms evidence included.
larsasub:prune-billables --orphaned (host-invoked, never auto-scheduled)
hard-deletes a registration whose owner no longer exists and through which no
money ever moved — the leftovers of a rolled-back provisional signup. Data
minimization favors deletion there: the rows tie to nobody reachable, and no
obligation remains that could ever need them. That includes the
larsasub_terms_acceptances rows: the acceptance evidences consent by an
owner that no longer exists for a subscription that never collected anything,
and the restrict FK leaves no way to keep it while removing the registration.
A registration with ANY financial history (a paid charge or payment, an
invoice, a credit note, a credit balance) is never pruned — its route is
anonymize(), which keeps the rows and strips the person. See
Artisan commands.
Retention
What the package logs is pruned on a schedule you control (the retention
block, see Configuration):
| Data | Kept | Pruned by |
|---|---|---|
| Model logs (the per-record activity trail) | retention.model_logs_days, default 730 days |
larsasub:prune-model-logs |
| Notification-delivery records | retention.notification_deliveries_days, default 365 days |
larsasub:prune-notification-deliveries |
| Resolved reminder series | retention.dunning_days, default null (kept) |
larsasub:prune-dunning-cases |
null or 0 disables a prune. The prunes run on the scheduler, so with
scheduling.enabled off nothing is pruned. Invoices, credit notes and paid
payments are never pruned.
What the operational tables hold
The reminder series (larsasub_dunning_cases, larsasub_dunning_steps).
Bookkeeping about how an unpaid charge was pursued: the charge and the billable
by id, the origin, the schedule, the status and why it paused or closed, and
per step the action, its result and when it ran. No name, address, amount or
message text — nothing a person can be read from without the billable it
points at, the same class as the charges themselves. anonymize() therefore
leaves them alone (it strips the person from the billable they point at), and
larsasub:prune-billables --orphaned deletes them with the registration.
Retention: retention.dunning_days, default null (kept), because the series
documents a debt collection; the prune never deletes a series whose charge is
still an unpaid debt.
A Connect OAuth flow while it runs (larsasub_connect_flow_states). This
one is transient rather than administrative: a row exists only between the
moment a tenant owner starts the Mollie authorization and the moment it
completes, is declined, or expires.
| Stored | Not stored |
|---|---|
The tenant the flow will connect (tenant_type + tenant_id) |
Anything about the tenant beyond that reference |
The initiating user's auth identifier (user_key) — who, and only who, may finish this flow |
Their name, address or any other attribute |
SHA-256 of the OAuth state and of the completion ticket |
Either secret in a form that could be replayed from a dump |
| The Mollie authorization code, encrypted at rest, between callback and completion | The code in the clear — and it is deleted the moment it is exchanged |
| The origin and return URL the flow started from |
There is no retention setting and no prune command, and expiry and deletion are two separate events:
- A flow that completes or is declined is deleted there and then.
- A flow that is abandoned — the tenant closes the tab at Mollie — stops being usable after 30 minutes, five once a ticket has been issued. Every lookup filters on that, so an expired row can never be used to connect anything.
- The row itself is removed by a sweep that runs when the next authorization starts. On an install whose tenants have all finished connecting, a small number of expired, unusable rows can therefore sit in the table until someone connects again. What they hold at that point is a tenant reference, an administrator's auth identifier, and an encrypted authorization code that was already dead when it expired.
anonymize() deliberately ignores this table — by the time erasure is a
question, a flow that concerned that billable is long gone, and a live one
belongs to a tenant administrator rather than to the subscriber being erased.
The notification-delivery log (larsasub_notification_deliveries), and
what it deliberately does not hold:
| Stored | Not stored |
|---|---|
Notification class (e.g. PaymentFailedNotification) |
Subject line |
| Channel, status, attempt count, timestamps | Message body, or any hash of it |
Recipient as a reference (recipient_type + recipient_id) |
The recipient's email address |
| A keyed HMAC-SHA256 hash for recipients that have no model | The address behind that hash |
| The exception class of a failed delivery | Its message — transport errors quote the address they could not reach |
| The billable and tenant the notification concerned | |
The random delivery_key of the queued job that produced the row |
(it identifies a job, not a person, and carries nothing derived from one) |
Laravel's notification_id, derived from that key and a keyed hash of the recipient |
(any form of the recipient that could be checked against a guess without the APP_KEY) |
Two choices worth stating, because they look like omissions:
- No subject. In this package the subject line is where the sensitive detail sits — invoice numbers, amounts, company names. The class answers "what was sent" without any of it, and does so in a form that survives translation.
- No content hash. It could not be verified: checking one requires rebuilding byte-identical content, and that content embeds balances and dates that have since moved on. It would be a column that never answers a question.
A delivery that is still in flight when anonymize() runs is recorded without
its billable link: the claim takes the same row lock erasure holds, so the two
serialise instead of racing.
Both hashes are keyed with APP_KEY — the recipient_hash column and the
fingerprint inside notification_id. The second matters more than it looks:
the id is stored next to the delivery_key it is built from, so an unkeyed
fingerprint would make the pair a verification oracle. Someone holding a
database dump could take an address they were curious about, recompute the id,
and a match would tell them that address was mailed — without ever needing the
key. Keying it means a guess cannot be checked. Rotating APP_KEY makes older
hashes unrecognisable; the only consequence is that a delivery in a retry
window could be sent twice.
Your obligations as controller
- Secure the database. Customer name, email, address and VAT/CoC number
live in plaintext on
larsasub_billablesandlarsasub_invoices. The copy on charges and single payments (vat_customer— country, profile type, VAT number, frozen so an invoice can justify the rate it printed), the provider detail bags and the mandate's IBAN are encrypted at rest.vat_customeris cleared byanonymize()and has no fiscal retention ground of its own: where an invoice exists, that invoice carries its own copy under the duty. - VIES receives the customer's VAT number (and yours, as requester)
whenever a check runs: with
invoicing.vat_validationon, after a change and per re-check interval — and, for a number VIES could not answer, again on each retry (up to five by the job, then twice a day by the scheduled sweep until it answers); and also while the check is off when you run the--uncheckedbackfill or callcheckNow(). Nothing else about the customer is sent, and VIES's name and address are not stored. Mention it in your privacy notice before you run either. - Mollie receives customer name and email. Do not assume a role: for payment services Mollie generally acts as an independent controller (it has its own legal duties around payments and fraud), while for some of its other services it acts as a processor. Which applies to you follows from the Mollie agreement you signed — check it, record the outcome in your Art. 30 register and describe the disclosure in your privacy notice. Mollie B.V. being EU-established does not by itself end the transfer analysis: their own sub-processors may sit outside the EEA, so read their current sub-processor list and safeguards rather than relying on their place of establishment.
- Erase at Mollie too.
anonymize()is local; delete the Mollie customer through Mollie's own tooling. - Data portability (Art. 20): build an export from the models if you need one.
APP_KEYrotation. The encrypted columns — OAuth tokens,vat_customer, the provider detail bags, the mandate's IBAN — are encrypted withAPP_KEY, and the package has no re-encryption command. Rotate only with the old key kept in Laravel'sAPP_PREVIOUS_KEYS, or those values become unreadable.- The delivery log names recipients by reference. Resolving
recipient_type/recipient_idis your query to write, and in multi-database tenancy it is only meaningful together with the row'stenant_type/tenant_id— owner ids repeat across tenant databases.
Payouts and third-party funds
The package never pays out and never holds money. Every payment goes from
the customer straight to a Mollie account — yours in simple mode, the tenant's
under Connect, where Mollie itself credits your application fee to your own
balance. There is no payout run, no balance per seller, no payment split
across sellers, and no money moved between Mollie balances;
larsasub:sync-settlements only mirrors the payouts Mollie makes itself.
Yours: if your platform collects money for others and pays it on, you hold funds that are not yours, which in the EU is generally a regulated payment service (a licence, or an exemption you establish yourself) — and a different product from this one. Under Connect that question does not arise in this form: each tenant is Mollie's own customer and passes Mollie's own verification (Onboarding status). See What LarsaSub fits, and what it does not.
Connect marketplaces
The package provides: per-tenant seller snapshots and per-tenant invoice sequences, so each tenant invoices as their own merchant of record.
Yours:
- The platform fee is never invoiced to the tenant — the dashboard shows it, but no tax document exists. Issue those invoices yourself (or agree self-billing).
- Every VAT item above applies to each tenant independently; each needs
their own seller details via
getInvoiceSellerDetails()on your tenant model. - DAC7: if tenants sell goods or personal services to consumers through your platform, you are likely a Reporting Platform Operator and must collect seller identification data the package does not model.