What Customer Data to Collect in Stripe to File to KSeF
Stripe collects the customer data and computes tax (Stripe Tax); KSeF Kit turns it into an FA(3) and files it to KSeF. Two fields drive the VAT treatment: customer_address.country and the buyer's tax id. From every customer, collect: whether it is a business (a valid tax id), the legal name, the country (most important), a full address, and the VAT/NIP. A missing or wrong number silently turns a B2B invoice into B2C — and that is not filed to KSeF.
You sell worldwide through Stripe — including into the EU — and you want invoices to land in KSeF with the right VAT. The key is simple: Stripe does not file to KSeF. Stripe collects the data, computes tax (Stripe Tax) and validates the format of a tax id. KSeF Kit builds the FA(3), picks the VAT treatment, renders the reverse-charge legend and files the document. So one thing matters: that Stripe holds the right data.
What actually drives the VAT treatment
KSeF Kit picks the treatment (VatTreatment) from customer_address.country and the buyer's tax ids
(customer_tax_ids):
- Domestic PL B2B — country
PLand apl_nip(or aneu_vatof the formPL+ 10 digits): an invoice at 23/8/5%, FA(3) fields P_13_1/2/3. - EU B2B — another EU country and the buyer's own
eu_vat: reverse charge, net only, P_18=1, FA(3) field P_13_9. - Non-EU export — a non-EU country and a foreign business tax id (e.g.
us_ein,gb_vat) and zero VAT: net only, FA(3) field P_13_8. - Filing refused (raises) — B2C or no usable tax id; an export still carrying PL VAT; a domestic 0%/exempt sale; a multi-rate invoice. The system fails loud rather than quietly filing a wrong 23% invoice.
The key practical takeaway: a missing or wrong tax id silently becomes B2C and is not filed to KSeF. So the data you collect in Stripe is not a formality — it is the condition for correct accounting.
Decision matrix: customer × location → treatment → data
| Customer + location | Treatment | What to collect |
|---|---|---|
| Business + PL | 23% domestic, to KSeF | name, address, NIP |
| Business + other EU | reverse charge (0%/np.), to KSeF | name, address, prefixed EU VAT no., country; invoice marked "reverse charge / odwrotne obciążenie" |
| Business + non-EU | outside EU VAT scope (e.g. art. 28b), to KSeF | name, address, country, local tax id; watch foreign GST/VAT registration |
| Consumer + PL | 23% | name, address |
| Consumer + other EU | customer-country VAT via OSS | address + 2 non-contradictory location evidences; the €10,000 threshold; outside KSeF |
| Consumer + non-EU | outside EU VAT scope / 0% | address, country; outside KSeF |
The two consumer rows (OSS and non-EU sales) do not go to KSeF — KSeF Kit files B2B. They are listed for a complete picture of your VAT, not because KSeF Kit handles them.
The minimum data set for every customer
- Whether it is a business — detected by the presence of a valid tax id.
- Legal name.
- Country — the most important field; it drives the whole logic.
- Full billing address —
line1,city,postal_code,country. - VAT/NIP/tax id — together with the stored VIES validation result and timestamp.
- For cross-border B2C — two location evidences and the VAT decision applied (an audit trail).
How to collect it in Stripe Checkout
In Stripe Checkout, enable address, tax-id and automatic-tax collection. The crucial piece is
customer_update — without it, Checkout data is not persisted to the Customer:
Stripe::Checkout::Session.create(
mode: "payment",
line_items: [ { price: price_id, quantity: 1 } ],
billing_address_collection: "required",
tax_id_collection: { enabled: true, required: "if_supported" },
automatic_tax: { enabled: true },
customer_update: { name: "auto", address: "auto" } # without this, data is not persisted
)
Creating a customer directly
When you create a customer outside Checkout, pass the name, address and tax id up front:
Stripe::Customer.create(
name: "Example Buyer Ltd",
address: { line1: "1 Example St", city: "Warsaw", postal_code: "00-001", country: "PL" },
tax_id_data: [ { type: "pl_nip", value: "1234563218" } ]
)
pl_nipis version-gated. It needs API version2026-01-28.clover+ and stripe-ruby ≥ 18.2.0. On older versions, store Polish companies aseu_vat(PL…) and strip thePLprefix when you build the FA(3).
Stripe through the Pay gem
The Pay gem forwards name and address via stripe_attributes (a method/lambda returning
{name:, address:}). It passes automatic_tax and Checkout params through
payment_processor.checkout(...) / .subscribe(...). Pay has no tax-id wrapper — pass tax_id_data
via stripe_attributes, or use payment_processor.api_record and the native Stripe tax-id API:
class User < ApplicationRecord
pay_customer stripe_attributes: :stripe_attributes
def stripe_attributes(_pay_customer)
{
name: company_name,
address: { line1: address_line1, city: city, postal_code: postal_code, country: country },
tax_id_data: [ { type: "eu_vat", value: vat_number } ] # Pay won't wrap tax-ids itself
}
end
end
Validation: Stripe checks format, KSeF needs more
Stripe validates format only — not legal validity. For KSeF that is not enough. KSeF Kit also:
- computes the Polish NIP checksum,
- runs the Ministry of Finance "biała lista" lookup for the seller,
- relies on a VIES-confirmed EU VAT number for reverse charge.
Professional advice is warranted. Some cases are genuinely nuanced: reverse charge without a confirmed VAT number, the €10,000 OSS threshold, and the need for foreign VAT/GST registration when selling outside the EU. These are tax decisions, not just technical ones.
Selling globally through Stripe?
Once the data in Stripe is complete, KSeF Kit recognises the transaction type and files the correct structured invoice — with VAT converted to złoty and a QR code on the visualisation. See the Stripe + KSeF integration and export invoices in KSeF.