Skip to main content
RapidDev - Software Development Agency
stripe-guide

How to collect billing details in Stripe Checkout

Collect billing details in Stripe Checkout using billing_address_collection: 'required' and custom_fields for additional inputs like company name or PO number. Stripe displays address fields on the checkout page and includes the data in the Checkout Session object for retrieval via webhook or API.

What you'll learn

  • How to require billing address collection during checkout
  • How to add custom fields like company name or PO number
  • How to retrieve collected billing details after payment
  • How to configure shipping address collection
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner5 min read10 minutesStripe API v2024-12+, Node.js 18+March 2026RapidDev Engineering Team
TL;DR

Collect billing details in Stripe Checkout using billing_address_collection: 'required' and custom_fields for additional inputs like company name or PO number. Stripe displays address fields on the checkout page and includes the data in the Checkout Session object for retrieval via webhook or API.

Collecting Billing and Shipping Details in Stripe Checkout

By default, Stripe Checkout only collects payment details and email. You can require a full billing address with billing_address_collection: 'required', add custom text or dropdown fields with custom_fields, and collect shipping addresses with shipping_address_collection. All collected data is stored on the Checkout Session and available via the API or webhooks after payment.

Prerequisites

  • A working Stripe Checkout Session endpoint
  • Node.js 18+ with the stripe npm package
  • Your Stripe secret key (sk_test_) in environment variables

Step-by-step guide

1

Enable billing address collection

Set billing_address_collection to 'required' when creating the Checkout Session. Stripe adds address fields (street, city, state, ZIP, country) to the checkout page.

typescript
1const session = await stripe.checkout.sessions.create({
2 mode: 'payment',
3 billing_address_collection: 'required',
4 line_items: [
5 {
6 price_data: {
7 currency: 'usd',
8 product_data: { name: 'Widget' },
9 unit_amount: 2000,
10 },
11 quantity: 1,
12 },
13 ],
14 success_url: 'https://yoursite.com/success?session_id={CHECKOUT_SESSION_ID}',
15 cancel_url: 'https://yoursite.com/cancel',
16});

Expected result: The Checkout page shows billing address fields that the customer must fill in before paying.

2

Add custom fields

Use custom_fields to add up to 3 additional text or dropdown inputs to the Checkout page. These are useful for collecting PO numbers, company names, or order notes.

typescript
1const session = await stripe.checkout.sessions.create({
2 mode: 'payment',
3 billing_address_collection: 'required',
4 custom_fields: [
5 {
6 key: 'company_name',
7 label: { type: 'custom', custom: 'Company Name' },
8 type: 'text',
9 optional: true,
10 },
11 {
12 key: 'po_number',
13 label: { type: 'custom', custom: 'PO Number' },
14 type: 'text',
15 optional: true,
16 },
17 ],
18 line_items: [{ price: 'price_xxx', quantity: 1 }],
19 success_url: 'https://yoursite.com/success?session_id={CHECKOUT_SESSION_ID}',
20 cancel_url: 'https://yoursite.com/cancel',
21});

Expected result: The Checkout page shows Company Name and PO Number fields above the payment details.

3

Enable shipping address collection

To collect a shipping address separately from the billing address, use shipping_address_collection with allowed_countries.

typescript
1const session = await stripe.checkout.sessions.create({
2 mode: 'payment',
3 billing_address_collection: 'required',
4 shipping_address_collection: {
5 allowed_countries: ['US', 'CA', 'GB', 'AU'],
6 },
7 line_items: [{ price: 'price_xxx', quantity: 1 }],
8 success_url: 'https://yoursite.com/success?session_id={CHECKOUT_SESSION_ID}',
9 cancel_url: 'https://yoursite.com/cancel',
10});

Expected result: The Checkout page shows both billing and shipping address sections. Shipping is limited to the specified countries.

4

Retrieve collected details after payment

After the customer pays, retrieve the Checkout Session to access billing details, shipping details, and custom field values.

typescript
1app.get('/api/order-details', async (req, res) => {
2 const session = await stripe.checkout.sessions.retrieve(
3 req.query.session_id
4 );
5
6 res.json({
7 email: session.customer_details?.email,
8 name: session.customer_details?.name,
9 billing_address: session.customer_details?.address,
10 shipping: session.shipping_details,
11 custom_fields: session.custom_fields, // Array of { key, text/dropdown value }
12 payment_status: session.payment_status,
13 });
14});

Expected result: The API returns all collected customer details including billing address, shipping address, and custom field values.

Complete working example

server.js
1const express = require('express');
2const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
3
4const app = express();
5app.use(express.static('public'));
6app.use(express.json());
7
8app.post('/create-checkout-session', async (req, res) => {
9 try {
10 const session = await stripe.checkout.sessions.create({
11 mode: 'payment',
12 billing_address_collection: 'required',
13 shipping_address_collection: {
14 allowed_countries: ['US', 'CA', 'GB', 'AU', 'DE', 'FR'],
15 },
16 custom_fields: [
17 {
18 key: 'company_name',
19 label: { type: 'custom', custom: 'Company Name' },
20 type: 'text',
21 optional: true,
22 },
23 {
24 key: 'order_notes',
25 label: { type: 'custom', custom: 'Order Notes' },
26 type: 'text',
27 optional: true,
28 },
29 ],
30 line_items: [
31 {
32 price_data: {
33 currency: 'usd',
34 product_data: { name: 'Premium Widget' },
35 unit_amount: 5000,
36 },
37 quantity: 1,
38 },
39 ],
40 success_url: `${req.headers.origin}/success?session_id={CHECKOUT_SESSION_ID}`,
41 cancel_url: `${req.headers.origin}/cancel`,
42 });
43
44 res.json({ url: session.url });
45 } catch (err) {
46 res.status(500).json({ error: err.message });
47 }
48});
49
50app.get('/api/order-details', async (req, res) => {
51 try {
52 const session = await stripe.checkout.sessions.retrieve(req.query.session_id);
53 res.json({
54 email: session.customer_details?.email,
55 name: session.customer_details?.name,
56 billing_address: session.customer_details?.address,
57 shipping: session.shipping_details,
58 custom_fields: session.custom_fields,
59 amount_total: session.amount_total,
60 payment_status: session.payment_status,
61 });
62 } catch (err) {
63 res.status(400).json({ error: 'Invalid session' });
64 }
65});
66
67app.listen(4000, () => console.log('Server on port 4000'));

Common mistakes when collecting billing details in Stripe Checkout

Why it's a problem: Assuming billing address is collected by default

How to avoid: By default, billing_address_collection is 'auto', meaning Stripe only asks for the address when the payment method requires it. Set it to 'required' to always collect it.

Why it's a problem: Adding more than 3 custom fields

How to avoid: Stripe Checkout supports a maximum of 3 custom fields per session. Keep additional data collection on your own pages before or after checkout.

Why it's a problem: Not specifying allowed_countries for shipping

How to avoid: shipping_address_collection requires an allowed_countries array. List only the countries you actually ship to.

Why it's a problem: Looking for custom field values in the wrong place

How to avoid: Custom field values are in session.custom_fields, not session.metadata. Each entry has a key and a text (or dropdown) object with the value.

Best practices

  • Set billing_address_collection: 'required' when you need the address for invoicing or tax calculation
  • Use custom_fields for essential business data (PO number, company name) — max 3 fields
  • Restrict shipping_address_collection to only the countries you serve
  • Retrieve billing details via webhook (checkout.session.completed) for reliable order processing
  • Use the phone_number_collection: { enabled: true } option if you need the customer's phone number
  • Test the full flow with card 4242 4242 4242 4242 and verify collected details in the Dashboard

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Write a Node.js Stripe Checkout Session that collects billing address (required), shipping address (US and Canada only), and two custom fields (Company Name and PO Number, both optional). Show how to retrieve all collected details after payment.

Stripe Prompt

Update my Stripe Checkout to collect billing address (required), shipping address for US/CA/GB, and a custom Company Name field. Add an endpoint to retrieve all collected customer details after payment using the session ID.

Frequently asked questions

Can I pre-fill the billing address?

Yes. If you create or pass a Stripe Customer with a known address, Checkout can pre-fill those fields. Pass the customer ID when creating the session.

Is phone number collection available?

Yes. Add phone_number_collection: { enabled: true } to the session creation params. The phone number is stored on session.customer_details.phone.

Can I make custom fields required?

Yes. Omit the optional: true property (or set optional: false). The customer must fill in the field before they can pay.

Do billing details work with subscription mode?

Yes. billing_address_collection, shipping_address_collection, and custom_fields all work with mode: 'subscription' the same way as mode: 'payment'.

What if I need to collect more complex data during checkout?

Stripe Checkout supports up to 3 custom fields. For more complex forms — multi-step checkout, conditional fields, or file uploads — RapidDev can help you build a custom checkout flow using Stripe Elements with your own form design.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.