Skip to main content

Address Verification (AVS)

Address Verification Service (AVS) checks the billing address a buyer enters against the address the card issuer has on file. Sending it with a payment is optional, but recommended for card-not-present payments:

  • Higher approval rates. Issuers are more likely to approve a charge that includes a matching billing address.
  • A fraud signal. A street or postal-code mismatch is a useful input to your own risk rules.
  • Better interchange. For many card-not-present programs, submitting AVS data is a condition of qualifying for the best interchange rate.

Vonpay collects the billing address in the payment fields, attaches it to the payment behind the scenes, and returns a normalized result you can read back. You never handle the address yourself — it is encrypted at rest and never returned in API responses, webhooks, or logs.

Availability

AVS activates per gateway and is opt-in: a payment only carries a billing address when your integration collects one. Integrations that don't collect a billing address are unaffected. Requires @vonpay/vora-js 1.9.2+ for the embedded fields. If your account's gateway hasn't enabled AVS yet, avs_result_code comes back null and nothing else changes.

Collect the billing address

How you collect it depends on your integration mode (integrationMode on session create).

Embedded fields — default ("embed")

In the default embed mode the billing address is collected inside the card field — there's nothing extra to mount. When the buyer completes it, it rides along with the payment automatically.

But by default they don't have to. That's the setting that decides whether you actually get AVS data.

Decide whether the address is optional or required

billingAddress on session create controls the billing section. It applies to hosted checkout and to embedded fields alike:

ValueWhat the buyer seesUse it when
"auto" (default)Section shown, optional — the buyer can leave it blank and still payYou want AVS when offered freely, and won't add friction to get it
"required"Buyer must complete it before the pay button enablesYou want AVS data reliably
"none"Section hidden entirelyYou never want a billing address — no AVS result
POST /v1/sessions
{
"amount": 4999,
"currency": "USD",
"billingAddress": "required"
}
If AVS matters to you, say "required"

On the default "auto", a buyer can skip the billing address and pay anyway — so a share of your payments will carry no address, and avs_result_code comes back null for those. That's not a failure, it's the setting working as designed.

Everything on this page — better approval rates, the fraud signal, interchange qualification — depends on the address actually being there. If you're reading this page, "required" is probably what you want.

The trade-off is real: a required field adds checkout friction and can cost conversions. Choose deliberately rather than leaving the default by accident.

Two related fields sit alongside it on the same call. phone uses the same none / auto / required vocabulary (default none), and collectEmail is a boolean (default false) that shows an email input even when you already supplied a buyer email — an email input appears automatically whenever none is on file, flag or not. Neither affects AVS; they're listed here so the collection controls are in one place. Full definitions in the create-session reference.

collectPhone is retired

It's now phone: "auto" (or "required"). The old flag is refused, not ignored — see the migration table.

Embedded fields — Elements ("elements")

In elements mode you place fields individually. Mount a billing address element alongside the card:

const elements = vora.elements.create();
const card = elements.create('card');
const address = elements.create('address', { mode: 'billing' });

card.mount('#card');
address.mount('#billing-address');

// On submit, the address is collected and sent with the card automatically.
const result = await elements.submit();

No extra wiring is required — elements.submit() gathers the address and forwards it with the token registration. See Custom checkout (Elements mode) for the full field layout.

Payments API (server to server)

If you vault or charge a card directly through the API, include a billing_address object on the request:

{
"amount": 1999,
"currency": "USD",
"payment_method": { "id": "vp_pmt_live_…" },
"billing_address": {
"address_line1": "1600 Pennsylvania Ave NW",
"city": "Washington",
"state": "DC",
"postal_code": "20500",
"country": "US"
}
}

Which fields to send

FieldRequiredNotes
address_line1YesStreet address. Drives the street-match portion of AVS.
postal_codeYesZIP / postal code. Drives the postal-match portion of AVS.
countryYesUppercase ISO 3166-1 alpha-2 (US, GB, …).
cityRecommendedImproves results with issuers that verify the full address.
stateRecommendedISO 3166-2 subdivision for the US (e.g. CA); free text elsewhere.
address_line2OptionalApartment, suite, unit, etc.

address_line1, postal_code, and country are the fields AVS actually checks — send at least those. Including city and state improves the result with issuers that verify the full address.

International

By default the embedded fields initialize for the United States. To collect AVS for buyers in other countries, set the buyer's country when you create the session:

{ "amount": 1999, "currency": "USD", "country": "GB" }

The address field then defaults to that country, and the buyer can still pick another. Always send country in the billing address itself — that's the value AVS verifies against.

Read the result

After a charge, the Payment Intent carries two normalized result fields:

FieldTypeDescription
avs_result_codestring | nullmatch, partial_postal (postal matched, street didn't), partial_address (street matched, postal didn't), no_match, unavailable (issuer returned nothing), or not_supported. null when no billing address was sent or no result was returned.
cvv_result_codestring | nullmatch, no_match, not_provided, or unavailable.

The same two fields appear on the charge.succeeded and charge.failed webhook events, so you can record or act on the result server-side.

{
"id": "vpi_live_…",
"status": "succeeded",
"amount": 1999,
"currency": "USD",
"card": { "brand": "visa", "last4": "4242" },
"avs_result_code": "match",
"cvv_result_code": "match"
}

The codes are normalized and gateway-independent — the same values regardless of which processor handled the charge. Raw processor response codes are not exposed through the API.

Decisioning is yours

Vonpay does not decline a payment on an AVS mismatch — a no_match charge can still succeed. Use avs_result_code / cvv_result_code in your own risk rules to decide whether to fulfil, review, or refund.

Adopting it

The billing-address forward is additive and opt-in. Existing integrations are unaffected — nothing is sent until you collect an address. To adopt:

  1. Upgrade @vonpay/vora-js to 1.9.2 or later (pinned URL or the auto-update channel).
  2. Collect the billing address (the card field already does in embed mode; mount an address element in elements mode).
  3. Read avs_result_code from the payment intent or your charge.* webhook.