Solving the hard parts of eCommerce
Symfony Live Madrid 2014
Presented by Bojan Zivanovic / @bojan_zivanovic
Our vision is for Drupal Commerce to be the number one
open source eCommerce platform in the world.
We're doing something right...
Paris | London | Ann Arbor, MI
Creators of Platform.sh and Drupal Commerce
Commerce 2.x Sprint
Once again, we start from scratch
Build me an address form!
Data model: Address
- Recipient, Organization
- Address line 1, Address line 2
- Postal code, Sorting code
- Locality, Dependent locality
- Administrative area, Country
Data model: Address format
- Which fields are used, and in which order
- Which fields are required
- Which fields need to be uppercased
- Field labels
- Regular expression for validating postal codes
Data model: Subdivision
- Hierarchical, up to 3 levels
- Multilingual user-facing name
- Code (used on envelopes, e.g. CA for California)
- Postal code validation pattern
- AddressType -> Field subscriber
- Gets the address format for the selected country, generates the fields
- Country code is valid.
- All required fields are filled in.
- All fields unused by the country's format are empty.
- All subdivisions are valid (values matched against predefined subdivisions).
- The postal code is valid (country and subdivision-level patterns).
- Formats an address according to the destination country format.
- Appends the country name in the local language
- Takes care of major-to-minor and minor-to-major field ordering.
- California and Nevada
- European Union
- Germany and a set of Austrian postal codes (6691, 6991, 6992, 6993)
- Austria without specific postal codes (6691, 6991, 6992, 6993)
Each zone consists out of zone members.
Special: include/exclude postal codes
Matching an address
- against a single zone: $zone->match($address)
- against all zones: ZoneMatcher
Problem #1: What are my rates?
Problem #1.1: 2015 VOES
- VAT on Electronically Supplied Services
- When selling a digital product, VAT is due at the place of the customer.
- From 2015 this applies to EVERYONE.
Problem #2: Percentages change
Problem #3: Tax zones
German VAT is used in Germany and 5 Austrian postal codes.
Austrian VAT is used in Austria without those 5 postal codes.
Problem #4: Charging the correct rate
- B2B or B2C?
- Physical or digital product?
- Registered for tax in additional countries?
- Place of supply
Zone 1-1 TaxType 1-n TaxRate 1-n TaxRateAmount
- Tax type: French VAT
- Zone: "France (VAT)" (inc. "France ex. Corsica" and "Monaco")
- Tax rates: Standard, Intermediate, Reduced, Super Reduced
- Tax rate amounts for Standard:
19.6% (until Jan 1st 2014), 20% (from Jan 1st 2014)
Bundled data for EU and Switzerland
- Resolve the tax types.
- Resolve the tax rate for each resolved tax type.
- Get the tax rate amount for each resolved tax rate.
Canada Tax Type Resolver
If selling from a store in Quebec to a customer in Ontario, apply the Ontario HST.
EU Tax Type Resolver
A French store selling physical products (e.g. t-shirts) will charge French VAT to EU customers.
A French store selling digital products (e.g. ebooks) from Jan 1st 2015 will
apply the EU customer's tax rates (German customer - German VAT, etc)
A French store will charge the 0% Intra-Community rate if the EU customer has provided a VAT number.
Default Tax Type Resolver
The Serbian store is selling to a Serbian customer, use Serbian VAT.
Default Tax Rate Resolver
Use the default rate ($rate->isDefault()) for the resolved tax type.
Or write your own!
No tax in New York for t-shirts under 200$
No tax for school supplies on september 1st (US tax holiday)
Reduced rate for ebooks in France.
The next payment API?
Each README points to a blog post on drupalcommerce.org.