Automatic card updates

How VINR refreshes saved card details when a bank re-issues a card, and how to handle brand changes.

View as MarkdownInstall skills

VINR works with card networks to automatically update saved card details when a bank re-issues a card — for example, when a card expires or is reported lost or stolen. This lets customers continue using your service without interruption and reduces the rate of failed charges due to stale card data.

How it worksAsk

When a card is re-issued, the issuing bank notifies the card network (Visa or Mastercard). The network passes updated details — new expiry date, and sometimes a new card number — to VINR. VINR applies the update to the stored PaymentMethod without any action required from you or the customer.

Coverage is broad across major Visa and Mastercard issuers in Bulgaria and the broader EEA.

Webhook eventsAsk

VINR fires webhook events when a card is updated:

  • payment_method.updated — a card was updated via a VINR API call.
  • payment_method.automatically_updated — the card network automatically updated a saved card.

Both events include the card's new expiration date and last four digits. If the update includes a new card number, the fingerprint changes.

// POST /webhooks/vinr
const event = vinr.webhooks.verify(rawBody, req.headers['x-vinr-signature']);

if (event.type === 'payment_method.automatically_updated') {
  const card = event.data.object.card;
  const prev = event.data.previousAttributes?.card;

  console.log(`Card updated: ...${card.last4}, expires ${card.expMonth}/${card.expYear}`);

  if (prev?.fingerprint && prev.fingerprint !== card.fingerprint) {
    // PAN changed — notify the customer that their card on file has been updated
    await notifyCustomer(event.data.object.customer, 'card_updated');
  }
}

Brand changesAsk

In some cases, a card re-issuance changes the card's brand — for example, a Visa card re-issued as Mastercard. This is detected by comparing brand in previousAttributes with the new brand on the updated card.

When a card's brand changes, you cannot charge it for any merchant-initiated transactions (MITs) until you obtain a new cardholder agreement. See Customer-initiated & Merchant-initiated transactions.

if (event.type === 'payment_method.automatically_updated') {
  const prevBrand = event.data.previousAttributes?.card?.brand;
  const newBrand  = event.data.object.card?.brand;

  if (prevBrand && newBrand && prevBrand !== newBrand) {
    // Block future MITs until re-authorization
    await flagForReAuthorization(event.data.object.customer);
    await notifyCustomer(event.data.object.customer, 'card_brand_changed');
  }
}

Updating your own recordsAsk

If you store card metadata (last 4 digits, expiry) in your own database for display purposes, sync it from the webhook payload rather than polling the API:

if (event.type === 'payment_method.automatically_updated') {
  const { id, card } = event.data.object;
  await db.paymentMethods.update(id, {
    last4: card.last4,
    expMonth: card.expMonth,
    expYear: card.expYear,
    brand: card.brand,
  });
}

See alsoAsk

Was this page helpful?
Edit on GitHub

Last updated on

On this page