Skip to content

Collect automatic tax billing addresses for non-card LPM forms - #13820

Draft
cttsai-stripe wants to merge 4 commits into
masterfrom
cttsai/automatic-tax-lpm-billing-address-prototype
Draft

Collect automatic tax billing addresses for non-card LPM forms#13820
cttsai-stripe wants to merge 4 commits into
masterfrom
cttsai/automatic-tax-lpm-billing-address-prototype

Conversation

@cttsai-stripe

Copy link
Copy Markdown
Contributor

Summary

When Checkout automatic tax uses the billing address and the merchant selects Automatic address collection, new non-card LPM forms now collect the country-specific minimum billing address fields needed for tax.

What changed

  • Resolves server-specified forms to one billing-country controller: preserves a normal address, promotes a canonical country-only field while preserving its restrictions and initial value, or inserts a tax-minimum address before mandate/footer elements when no address exists.
  • Extends Simple definitions with a country-only operation, used by Klarna and Wero, and preserves a normal address when an LPM already requires one.
  • Adds a CheckoutController manual profile for Cash App Pay in USD, alongside Klarna, with automatic tax, no shipping, no Link, and Automatic address collection.
flowchart LR
    S[Resolved LPM form] --> D{Existing address shape}
    D -->|Normal address| K[Keep one normal address]
    D -->|Canonical country only| P[Promote to tax-minimum address]
    D -->|No address| A[Add tax-minimum address before footer]
Loading

What stays the same

  • Card keeps its existing automatic-tax collection path.
  • Full, Never, and tax-disabled Automatic address collection keep their existing form behavior.
  • Saved payment methods, US Bank Account, Link, Google Pay, custom payment methods, and external payment methods remain out of scope.
  • There are no public API or backend schema changes.

Motivation

Some non-card LPM forms collect only country or no billing address in Automatic mode. A Checkout Session that calculates tax from billing details can then require an address without providing fields that satisfy the requirement.

Testing

  • Added tests
  • Modified tests
  • Manually verified

Focused tests passed:

  • :payments-ui-core:testDebugUnitTest, filtered to AutomaticTaxBillingFieldsTest.
  • :paymentsheet:testDebugUnitTest, filtered to resolver, builder, Klarna, Wero, Blik, and registry tests.
  • ./gradlew :payments-ui-core:detekt :paymentsheet:detekt :paymentsheet-example:detekt :paymentsheet-example:assembleDebug.
  • Follow-up FormElementsBuilderTest plus :paymentsheet:detekt.
  • Follow-up ./gradlew :paymentsheet-example:detekt :paymentsheet-example:assembleDebug after the Cash App example correction.
  • git diff --check.

Manual Cash App verification:

  • The initial Checkout Session required a billing address for tax.
  • After entering a US billing address, the customer address was present and tax_meta.status was complete.
  • Cash App confirmation returned its expected redirect action.
  • The tax amount remained zero because the session reported automatic_tax_taxability_reason = not_collecting and taxable_amount = 0. This reflects the playground account's tax registration, not SDK address collection.

Screenshots

Not included. The form behavior was verified manually and with focused form-shape tests.

Changelog

  • [ADDED] Checkout automatic tax now collects the minimum billing address fields required for new non-card payment method forms rendered by the LPM form system.

cttsai-stripe and others added 4 commits August 6, 2026 23:42
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
Committed-By-Agent: codex
Co-authored-by: codex <noreply@openai.com>
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Diffuse output:

OLD: paymentsheet-example-release-master.apk (signature: V1, V2)
NEW: paymentsheet-example-release-pr.apk (signature: V1, V2)

          │            compressed            │           uncompressed           
          ├───────────┬───────────┬──────────┼───────────┬───────────┬──────────
 APK      │ old       │ new       │ diff     │ old       │ new       │ diff     
──────────┼───────────┼───────────┼──────────┼───────────┼───────────┼──────────
      dex │   4.6 MiB │   4.6 MiB │ +1,000 B │   9.8 MiB │   9.8 MiB │ +4.7 KiB 
     arsc │   3.8 MiB │   3.8 MiB │ +1.5 KiB │   3.8 MiB │   3.8 MiB │ +1.5 KiB 
 manifest │   5.8 KiB │   5.8 KiB │     -1 B │  30.9 KiB │  30.9 KiB │      0 B 
      res │     1 MiB │     1 MiB │     -4 B │   1.7 MiB │   1.7 MiB │      0 B 
   native │   2.7 MiB │   2.7 MiB │      0 B │   2.7 MiB │   2.7 MiB │      0 B 
    asset │  19.5 KiB │  21.1 KiB │ +1.6 KiB │  29.5 KiB │  31.1 KiB │ +1.6 KiB 
    other │ 245.5 KiB │ 245.5 KiB │    -21 B │ 505.4 KiB │ 505.4 KiB │      0 B 
──────────┼───────────┼───────────┼──────────┼───────────┼───────────┼──────────
    total │  12.4 MiB │  12.4 MiB │   +4 KiB │  18.6 MiB │  18.6 MiB │ +7.8 KiB 

 DEX     │ old   │ new   │ diff              
─────────┼───────┼───────┼───────────────────
   files │     1 │     1 │   0               
 strings │ 45691 │ 45714 │ +23 (+63 -40)     
   types │ 14418 │ 14426 │  +8 (+45 -37)     
 classes │ 11732 │ 11739 │  +7 (+17 -10)     
 methods │ 63888 │ 63908 │ +20 (+5208 -5188) 
  fields │ 41758 │ 41764 │  +6 (+2454 -2448) 

 ARSC    │ old  │ new  │ diff       
─────────┼──────┼──────┼────────────
 configs │  320 │  320 │  0         
 entries │ 7502 │ 7504 │ +2 (+2 -0)
APK
     compressed      │     uncompressed     │                                
──────────┬──────────┼───────────┬──────────┤                                
 size     │ diff     │ size      │ diff     │ path                           
──────────┼──────────┼───────────┼──────────┼────────────────────────────────
    9 KiB │ +1.6 KiB │   8.9 KiB │ +1.6 KiB │ ∆ assets/dexopt/baseline.prof  
  3.8 MiB │ +1.5 KiB │   3.8 MiB │ +1.5 KiB │ ∆ resources.arsc               
  4.6 MiB │ +1,000 B │   9.8 MiB │ +4.7 KiB │ ∆ classes.dex                  
 55.6 KiB │    -43 B │ 130.7 KiB │      0 B │ ∆ META-INF/MANIFEST.MF         
 58.9 KiB │    +20 B │ 130.8 KiB │      0 B │ ∆ META-INF/CERT.SF             
  1.2 KiB │     -3 B │   1.1 KiB │     -3 B │ ∆ assets/dexopt/baseline.profm 
    435 B │     -3 B │     744 B │      0 B │ ∆ res/RM.xml                   
  1.5 KiB │     +3 B │     8 KiB │      0 B │ ∆ res/kw.xml                   
    435 B │     -2 B │     744 B │      0 B │ ∆ res/-1.xml                   
  1.5 KiB │     +2 B │   6.3 KiB │      0 B │ ∆ res/4U.xml                   
    409 B │     -2 B │     592 B │      0 B │ ∆ res/KT.xml                   
    435 B │     -2 B │     744 B │      0 B │ ∆ res/QZ.xml                   
  1.1 KiB │     +2 B │   3.3 KiB │      0 B │ ∆ res/nn.xml                   
  1.2 KiB │     +2 B │   1.2 KiB │      0 B │ ∆ META-INF/CERT.RSA            
  5.8 KiB │     -1 B │  30.9 KiB │      0 B │ ∆ AndroidManifest.xml          
    798 B │     -1 B │   1.6 KiB │      0 B │ ∆ res/01.xml                   
    837 B │     +1 B │   1.8 KiB │      0 B │ ∆ res/0H.xml                   
    597 B │     -1 B │   1.1 KiB │      0 B │ ∆ res/2w.xml                   
    786 B │     +1 B │   1.5 KiB │      0 B │ ∆ res/3O.xml                   
  1.6 KiB │     -1 B │   4.7 KiB │      0 B │ ∆ res/5M.xml                   
  1.2 KiB │     +1 B │   3.9 KiB │      0 B │ ∆ res/8Q1.xml                  
    835 B │     +1 B │   1.8 KiB │      0 B │ ∆ res/AS.xml                   
  1.2 KiB │     +1 B │   3.9 KiB │      0 B │ ∆ res/BH.xml                   
    406 B │     -1 B │     592 B │      0 B │ ∆ res/BT.xml                   
    785 B │     -1 B │   1.6 KiB │      0 B │ ∆ res/Bh.xml                   
    410 B │     +1 B │     592 B │      0 B │ ∆ res/Ha.xml                   
    977 B │     -1 B │   2.9 KiB │      0 B │ ∆ res/Hp.xml                   
    595 B │     -1 B │   1.1 KiB │      0 B │ ∆ res/J7.xml                   
  1.1 KiB │     +1 B │   2.7 KiB │      0 B │ ∆ res/L-.xml                   
    406 B │     -1 B │     592 B │      0 B │ ∆ res/PQ.xml                   
    709 B │     -1 B │   1.3 KiB │      0 B │ ∆ res/QN.xml                   
    866 B │     -1 B │   1.9 KiB │      0 B │ ∆ res/Uc.xml                   
    407 B │     +1 B │     592 B │      0 B │ ∆ res/ZC.xml                   
    692 B │     +1 B │   1.7 KiB │      0 B │ ∆ res/fu.xml                   
    687 B │     +1 B │   1.3 KiB │      0 B │ ∆ res/hR.xml                   
    377 B │     -1 B │     724 B │      0 B │ ∆ res/k9.xml                   
    597 B │     -1 B │   1.1 KiB │      0 B │ ∆ res/pw.xml                   
    377 B │     -1 B │     724 B │      0 B │ ∆ res/sX.xml                   
    911 B │     +1 B │   2.8 KiB │      0 B │ ∆ res/uG.xml                   
    619 B │     -1 B │   1.2 KiB │      0 B │ ∆ res/uR.xml                   
    382 B │     -1 B │     548 B │      0 B │ ∆ res/vf.xml                   
    597 B │     +1 B │   1.2 KiB │      0 B │ ∆ res/vs.xml                   
    350 B │     -1 B │     516 B │      0 B │ ∆ res/xY.xml                   
    377 B │     +1 B │     724 B │      0 B │ ∆ res/yT.xml                   
    539 B │     +1 B │     952 B │      0 B │ ∆ res/zd.xml                   
──────────┼──────────┼───────────┼──────────┼────────────────────────────────
  8.6 MiB │   +4 KiB │    14 MiB │ +7.8 KiB │ (total)
DEX
STRINGS:

   old   │ new   │ diff          
  ───────┼───────┼───────────────
   45691 │ 45714 │ +23 (+63 -40) 
  
  + , intentFactory=
  + BillingAddressSpec(allowedCountryCodes=
  + Cash App Pay
  + CashApp
  + CountryOnly(allowedCountryCodes=
  + Existing(formElement=
  + ExistingSpec(spec=
  + Expected initial tax status RequiresBillingAddress, was 
  + Lad/h0;
  + Lio/sentry/c7;
  + Lj0/h1;
  + Lp5/l;
  + Luj/g;
  + Lv5/f;
  + Lx4/f;
  + Lxc/k;
  + Ly0/u0;
  + Lz/a2;
  + Lzh/h;
  + Lzh/i;
  + Lzh/j;
  + Lzh/k;
  + Lzh/l;
  + Lzh/m;
  + Lzh/n;
  + Manual profile: 
  + Tax status: 
  + VIILLLI
  + VLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
  + [Lio/sentry/clientreport/d;
  + [Lio/sentry/f6;
  + [Lio/sentry/h5;
  + [Lio/sentry/i6;
  + [Lio/sentry/k6;
  + [Lio/sentry/l5;
  + [Lio/sentry/p6;
  + [Lio/sentry/q5;
  + [Lio/sentry/v6;
  + [Lql/i0;
  + [Lql/m0;
  + [Lql/p0;
  + [Lql/u0;
  + [Lql/v;
  + [Luj/f;
  + [Lwd/b;
  + [Lx4/e;
  + [Lxh/c;
  + [Ly/o0;
  + [Lyi/t;
  + [Lyi/x;
  + [Lyj/e;
  + [Lz0/j1;
  + [Lz0/j5;
  + [Lz0/q2;
  + [Lz0/q3;
  + [Lz0/w1;
  + [Lzh/e;
  + additionalFieldsByCountry
  + checkout_controller_profile
  + displayFields
  + null cannot be cast to non-null type com.stripe.android.ui.core.elements.CountrySpec
  + r8-map-id-32b064f113ab25b3e952702a88bcd5800adcf9e4ed214e06d64aa09e1f2cd84f
  + ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":23,"pg-map-id":"32b064f113ab25b3e952702a88bcd5800adcf9e4ed214e06d64aa09e1f2cd84f","r8-mode":"full","version":"8.13.19"}
  
  - Lf4/o;
  - Lio/sentry/android/core/internal/debugmeta/a;
  - Lio/sentry/clientreport/g;
  - Lql/v0;
  - Lul/f;
  - Lv6/h0;
  - Lwd/g;
  - Lxh/h;
  - Lyi/j0;
  - Lz0/s5;
  - VLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
  - [Lio/sentry/clientreport/e;
  - [Lio/sentry/e6;
  - [Lio/sentry/f5;
  - [Lio/sentry/h6;
  - [Lio/sentry/j6;
  - [Lio/sentry/k5;
  - [Lio/sentry/o6;
  - [Lio/sentry/p5;
  - [Lio/sentry/u6;
  - [Lql/b0;
  - [Lql/l0;
  - [Lql/n0;
  - [Lql/q0;
  - [Lql/v0;
  - [Luj/d;
  - [Lwd/c;
  - [Lx4/d;
  - [Lxh/g;
  - [Ly/n0;
  - [Lyi/u;
  - [Lyi/y;
  - [Lz0/k1;
  - [Lz0/k5;
  - [Lz0/r2;
  - [Lz0/r3;
  - [Lz0/x1;
  - [Lzh/d;
  - r8-map-id-3848f37d63deeed1ff0dfd50e01fa1097b40c1d6724a8288c7cea34024cc27b1
  - ~~R8{"backend":"dex","compilation-mode":"release","has-checksums":false,"min-api":23,"pg-map-id":"3848f37d63deeed1ff0dfd50e01fa1097b40c1d6724a8288c7cea34024cc27b1","r8-mode":"full","version":"8.13.19"}
  

TYPES:

   old   │ new   │ diff         
  ───────┼───────┼──────────────
   14418 │ 14426 │ +8 (+45 -37) 
  
  + Lad/h0;
  + Lio/sentry/c7;
  + Lj0/h1;
  + Lp5/l;
  + Luj/g;
  + Lv5/f;
  + Lx4/f;
  + Lxc/k;
  + Ly0/u0;
  + Lz/a2;
  + Lzh/h;
  + Lzh/i;
  + Lzh/j;
  + Lzh/k;
  + Lzh/l;
  + Lzh/m;
  + Lzh/n;
  + [Lio/sentry/clientreport/d;
  + [Lio/sentry/f6;
  + [Lio/sentry/h5;
  + [Lio/sentry/i6;
  + [Lio/sentry/k6;
  + [Lio/sentry/l5;
  + [Lio/sentry/p6;
  + [Lio/sentry/q5;
  + [Lio/sentry/v6;
  + [Lql/i0;
  + [Lql/m0;
  + [Lql/p0;
  + [Lql/u0;
  + [Lql/v;
  + [Luj/f;
  + [Lwd/b;
  + [Lx4/e;
  + [Lxh/c;
  + [Ly/o0;
  + [Lyi/t;
  + [Lyi/x;
  + [Lyj/e;
  + [Lz0/j1;
  + [Lz0/j5;
  + [Lz0/q2;
  + [Lz0/q3;
  + [Lz0/w1;
  + [Lzh/e;
  
  - Lf4/o;
  - Lio/sentry/android/core/internal/debugmeta/a;
  - Lio/sentry/clientreport/g;
  - Lql/v0;
  - Lul/f;
  - Lv6/h0;
  - Lwd/g;
  - Lxh/h;
  - Lyi/j0;
  - Lz0/s5;
  - [Lio/sentry/clientreport/e;
  - [Lio/sentry/e6;
  - [Lio/sentry/f5;
  - [Lio/sentry/h6;
  - [Lio/sentry/j6;
  - [Lio/sentry/k5;
  - [Lio/sentry/o6;
  - [Lio/sentry/p5;
  - [Lio/sentry/u6;
  - [Lql/b0;
  - [Lql/l0;
  - [Lql/n0;
  - [Lql/q0;
  - [Lql/v0;
  - [Luj/d;
  - [Lwd/c;
  - [Lx4/d;
  - [Lxh/g;
  - [Ly/n0;
  - [Lyi/u;
  - [Lyi/y;
  - [Lz0/k1;
  - [Lz0/k5;
  - [Lz0/r2;
  - [Lz0/r3;
  - [Lz0/x1;
  - [Lzh/d;
  

METHODS:

   old   │ new   │ diff              
  ───────┼───────┼───────────────────
   63888 │ 63908 │ +20 (+5208 -5188) 
  
  + a.a T(k4, d0, u0)
  + a1.a l(Object, Object) → Object
  + a1.c l(Object, Object) → Object
  + a1.d h(Object) → Object
  + a1.d j(Object) → Object
  + a1.h l(Object, Object) → Object
  + a1.p a(a, k1, d, s, int)
  + a1.q l(Object, Object) → Object
  + a1.t <init>(a, View, c, k1, UUID)
  + a1.t h(a, q)
  + a2.a a(v4, AndroidComposeView, boolean) → int
  + a5.a g(SecretKeySpec, byte[], byte[], byte[], byte[], byte[]) → SecretKeySpec
  + a5.a h(int) → int
  + a5.a i(View, z1) → z1
  + a6.a0 A(d) → Map
  + a6.a0 B(g)
  + a6.a0 C(n)
  + a6.a0 D(String, Bundle) → Bundle
  + a6.a0 E()
  + a6.a
...✂
ARSC
ENTRIES:

   old  │ new  │ diff       
  ──────┼──────┼────────────
   7502 │ 7504 │ +2 (+2 -0) 
  + string/checkout_controller_cash_app_example_subtitle
  + string/checkout_controller_cash_app_example_title

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant