Account Updater

Spreedly’s Account Updater allows you to always keep your customers’ card details up-to-date. When your customers’ credit card numbers expire or are updated, Account Updater protects you from the lost revenue, involuntary churn, and decreased customer satisfaction associated with outdated payment information. No additional development is required on your side since cards are updated behind the scenes.

How Account Updater Works

When you opt-in, Spreedly defaults to submitting all retained, non-test, Visa, MasterCard, and Discover cards in your Spreedly vault for automatic updating twice monthly, on the 1st and 15th of every month. The table below lists current regional participation by card brand:

Supported Schemes Market Coverage
Visa
  • North America: Near full participation.
  • Europe: Near full participation in UK, Ireland, Italy, Greece. High participation elsewhere, excluding Hungary and Turkey.
  • Central Europe, Middle East, Africa: High participation.
Mastercard
  • North America: near full participation.
  • Europe: Near full participation in UK, Ireland, and Italy. High participation elsewhere.
  • Latin America: High participation
  • Asia Pacific: High participation, excluding India.
  • Central Europe, Middle East, Africa: High participation.
Discover
  • North America: Near full participation.

Account Updater Control Levels

Organizational-Level Controls

Opting in to Account Updater happens at the organization level - all environments will be affected. By enabling the Account Updater feature, all eligible cards will be sent for update twice monthly, but you will only be charged for those cards that are identified to be updated. Turning Account Updater off at the organizational level will not affect an individual card’s eligibility status.

Environment-Level Controls

For more granular control, you can opt for specific environments to have their cards sent to Account Updater while excluding other environments from card updates. In order to enable Account Updater at the environment level, it must first be enabled for an organization.

Managing Environments for Account Updater

  1. Given Account Updater is enabled for the organization, toggle “Allow Account Updater at Environment Level” in the Organization Settings. This setting can be toggled in Spreedly ID at the following URL once you are logged in: https://id.spreedly.com/organizations/{organization_key}/edit. Once this is enabled, only environments specifically toggled in the UI or are marked as au_enabled will be sent to Account Updater (see the step below).

  2. You may now enable individual environments via the UI under each environment by toggling the setting “Environment Level Account Updater Enabled” (the property on the Environment model is au_enabled). Alternatively, environments can be enabled via the environment create and update endpoints by passing the field au_enabled: true in the body.

    Note: This setting is disabled by default for all environments. Thus, if only “Allow Account Updater at Environment Level” is enabled in the Organization Settings, none of the environments will participate in the Account Updater process until they are specifically enabled.

Card-Level Controls

You can optionally exclude individual cards from the Account Updater service via our API. For example, you may want to exclude a card if your customer has paused their subscription. Payment methods include an eligible_for_card_updater field that when set to false will result in Spreedly excluding the associated card from subsequent submissions to the Account Updater service. You can set this field to true to resume submitting the given card at any time.

Note: Payment methods that have the eligible_for_card_updater field set to false will continue to be excluded from being updated by Account Updater regardless of the Environment level Account Updater enablement settings.

Configuration Matrix

Org AU Allow AU at Environment Level au_enabled (Environment) eligible_for_card_updater (Payment Method) Expected results
Off N/A N/A True Card is eligible but not sent to AU because AU is Off for the Organization
On Off N/A True All cards in the organization with eligible_for_card_updater: true are sent to AU
On On False True AU is On at the environment level but the specific environment is not au_enabled. No cards are sent from that environment regardless of eligibility
On On True True All environments with au_enabled: true or “Environment Level Account Updater Enabled” toggled on will have all eligibile cards sent to AU
On On/Off True/False False Cards with eligible_for_card_updater: false will not be sent to AU

Analyze Results

Dashboard

Results of Account Updater submissions can be tracked in your Spreedly dashboard. You can view summary statistics for the three most recent submissions (or “batches”). You can also drill into detailed per-card results and download a CSV report for any given batch right from the dashboard. The status for each card is shown:

  1. ReplacePaymentMethod - The number and/or expiration date has been updated.
  2. InvalidReplacePaymentMethod - We received either a new card number or expiration date that was invalid. The payment method was not updated, and there is no charge.
  3. ContactCardHolder - The card network identified this account needs to be updated but the issuer did not provide the updated values. Contact the cardholder for a new number and/or expiration date.
  4. ClosePaymentMethod - The account is no longer open and should no longer be used. The payment method will remain available, but Spreedly’s Account Updater will no longer attempt to update it.

Custom Analytics

Spreedly also provides Custom Analytics that allows the customer to build their own dashboard and visualizations that best suits their needs. For more details on implementing, please see the Custom Analytics for Dashboard.

Callbacks

As an alternative to viewing results in the Spreedly dashboard, callbacks are provided that will push Account Updater results to a destination of your choosing. These notifications are delivered via an HTTPS POST to a public-facing URL on your server. This should be a URL to your system that is able to receive a POST request with a list of transactions whose state has been updated.

Since Account Updater operates in a batch manner and card updates may result in a large number of transactions, Spreedly only delivers callbacks every few minutes. A single callback may contain up to 150 transactions with a maximum delivery of approximately 500 callbacks within a 24 hour period per environment when using an environment based callback URL or per URL when using an override.

The incoming callback POST requests will be structured as follows. Request bodies are formatted identically for all request types, varying only in the value of the transaction_type field.

{
  "transactions": [
    {
      "token": "S6cl5f11LqdVLe9m1TtQGMSMUuz",
      "created_at": "2019-02-13T13:40:43Z",
      "updated_at": "2019-02-13T13:42:48Z",
      "succeeded": true,
      "transaction_type": "ReplacePaymentMethod",
      "state": "succeeded",
      "account_key": "2H2HmVvjh171NvjCzr37D7jWG6c",
      "environment_key": "DDB58wpfgxHA1qD7p0LkqCLBnZw",
      "message_key": "messages.transaction_succeeded",
      "message": "Succeeded!",
      "payment_method": {
        "token": "EjpBzUVNHWnF1Y0MNaefuNpXwyR",
        "created_at": "2019-02-13T13:27:53Z",
        "updated_at": "2019-02-13T13:40:43Z",
        "email": "mm@example.com",
        "data": null,
        "storage_state": "retained",
        "test": false,
        "metadata": null,
        "callback_url": "https://7fba2acd.ngrok.io",
        "last_four_digits": "0003",
        "first_six_digits": "511201",
        "card_type": "master",
        "first_name": "Mighty",
        "last_name": "Mouse",
        "month": 1,
        "year": 2050,
        "address1": null,
        "address2": null,
        "city": null,
        "state": null,
        "zip": null,
        "country": null,
        "phone_number": null,
        "company": null,
        "full_name": "Mighty Mouse",
        "eligible_for_card_updater": true,
        "shipping_address1": null,
        "shipping_address2": null,
        "shipping_city": null,
        "shipping_state": null,
        "shipping_zip": null,
        "shipping_country": null,
        "shipping_phone_number": null,
        "payment_method_type": "credit_card",
        "errors": [],
        "fingerprint": "84fd610e28258201fffea68dc0f9e7c66859",
        "verification_value": "",
        "number": "XXXX-XXXX-XXXX-0003"
      },
      "signed": {
        "signature": "03268a0a7ccb7a1e18cd99f60513ec9814f9d240",
        "fields": "token created_at updated_at succeeded transaction_type state",
        "algorithm": "sha1"
      }
    },
    {
      "token": "I5kuefCHe8Tvm9HypLRLIKYHCmm",
      "created_at": "2019-02-13T13:40:40Z",
      "updated_at": "2019-02-13T13:42:48Z",
      "succeeded": true,
      "transaction_type": "ClosePaymentMethod",
      "state": "succeeded",
      "account_key": "2H2HmVvjh171NvjCzr37D7jWG6c",
      "environment_key": "DDB58wpfgxHA1qD7p0LkqCLBnZw",
      "message_key": "messages.transaction_succeeded",
      "message": "Succeeded!",
      "payment_method": {
        "token": "7xHGM9h7a8OoUboXPNHvY5pihgv",
        "created_at": "2019-02-13T13:27:56Z",
        "updated_at": "2019-02-13T13:40:40Z",
        "email": "mm@example.com",
        "data": null,
        "storage_state": "retained",
        "test": false,
        "metadata": null,
        "callback_url": "https://7fba2acd.ngrok.io",
        "last_four_digits": "0002",
        "first_six_digits": "601101",
        "card_type": "discover",
        "first_name": "Mighty",
        "last_name": "Mouse",
        "month": 8,
        "year": 1950,
        "address1": null,
        "address2": null,
        "city": null,
        "state": null,
        "zip": null,
        "country": null,
        "phone_number": null,
        "company": null,
        "full_name": "Mighty Mouse",
        "eligible_for_card_updater": false,
        "shipping_address1": null,
        "shipping_address2": null,
        "shipping_city": null,
        "shipping_state": null,
        "shipping_zip": null,
        "shipping_country": null,
        "shipping_phone_number": null,
        "payment_method_type": "credit_card",
        "errors": [],
        "fingerprint": "2b4184e42067002e39fb3f5a7e9275f34b17",
        "verification_value": "",
        "number": "XXXX-XXXX-XXXX-0002"
      },
      "signed": {
        "signature": "808eada381675ff34c3d0b67b010dda965445451",
        "fields": "token created_at updated_at succeeded transaction_type state",
        "algorithm": "sha1"
      }
    }
  ]
}

Note that each transaction contained in a callback is signed; this allows you to process the results of the callback without having to round trip back to Spreedly (though you certainly can round trip if you’d like - see below). Since an attacker could call your callback url with a valid looking transaction, the signature allows you to verify that the information in each transaction is really coming from Spreedly. In the example callback request shown above, the signatures were created using the following signing secret:


4ziASKWGGV1zdrUbSN6vq2CjjDPw2hzJSvsGLhxces1aORBKKsRyJwb8DfGQ6J3q

Here is an example of ruby code you could use to verify the signatures in the above callback request (with most of the fields omitted for brevity):


require 'openssl'

def signature_for(secret, transaction)
  algorithm = transaction['signed']['algorithm']

  fields = transaction['signed']['fields'].split(' ')

  values = fields.collect { |field| transaction[field] }

  signature_data = values.join("|")
  signature = OpenSSL::HMAC.hexdigest(
    OpenSSL::Digest.new(algorithm),
    secret,
    signature_data
  )
end

document = JSON.parse(
  %(
    {
      "transactions": [
        {
          "token": "S6cl5f11LqdVLe9m1TtQGMSMUuz",
          "created_at": "2019-02-13T13:40:43Z",
          "updated_at": "2019-02-13T13:42:48Z",
          "succeeded": true,
          "transaction_type": "ReplacePaymentMethod",
          "state": "succeeded",
          "signed": {
            "signature": "03268a0a7ccb7a1e18cd99f60513ec9814f9d240",
            "fields": "token created_at updated_at succeeded transaction_type state",
            "algorithm": "sha1"
          }
        },
        {
          "token": "I5kuefCHe8Tvm9HypLRLIKYHCmm",
          "created_at": "2019-02-13T13:40:40Z",
          "updated_at": "2019-02-13T13:42:48Z",
          "succeeded": true,
          "transaction_type": "ClosePaymentMethod",
          "state": "succeeded",
          "signed": {
            "signature": "808eada381675ff34c3d0b67b010dda965445451",
            "fields": "token created_at updated_at succeeded transaction_type state",
            "algorithm": "sha1"
          }
        }
      ]
    }
  )
)

document['transactions'].each do |transaction|
  puts signature_for(
    "4ziASKWGGV1zdrUbSN6vq2CjjDPw2hzJSvsGLhxces1aORBKKsRyJwb8DfGQ6J3q",
    transaction
  )
end

"03268a0a7ccb7a1e18cd99f60513ec9814f9d240"
"808eada381675ff34c3d0b67b010dda965445451"

You can find more details about signed requests in our signing reference.

We recognize that some customers are not interested in going through the trouble of writing code to validate the signature of the callback response. In this case, you can use the tokens of the transactions you receive in the callback and then make an authenticated API call to retrieve the details of each transaction. Because that call is authenticated and you’re making the request, there’s no need to verify where the information is coming from.

Callback Response

You should respond back with a 200 OK response within 5 seconds. If Spreedly does not receive back a 200 response within this time, it will retry the callback again at least 4 times at ever-increasing intervals. If you need to do potentially time-consuming operations when a callback is received, we recommend doing them asynchronously to avoid being timed out. Additionally, callbacks should be treated as idempotent since they may be sent more than once.

If you are not receiving callbacks, ensure that the callback URL provided utilizes HTTPS with standard port 443.

Getting started with callbacks

To get started with callbacks on Account Updater, a callback URL must be added to your Spreedly environment. Log in, select the environment you wish configure, and add a callback URL. If you store payment methods in multiple environments, a callback URL must be configured for each environment.

Additionally, Spreedly provides the ability to override the per-environment level callback URL with a payment method specific callback URL. This override is useful in situations where card update notifications are to be delivered to a location different from the environment level callback URL. To override the environment level callback URL, both the create payment method and update payment method API calls include a callback_url property that can be set. Once set, any update notifications related to this payment method will be delivered to the callback_url set on the payment method instead of the environment. By default, the callback_url property on a payment method is null and will only take precedence over the environment level setting when set. Spreedly does not update all of your payment methods when the environment level setting is present.

Fields

The following fields on a payment method can be changed by the Account Updater service with data directly from the card schemes:

  • number: The obscured new credit card number returned by Account Updater
  • month: The new expiration month returned by Account Updater
  • year: The new expiration year returned by Account Updater

The following fields on a payment method can be changed by Spreedly based on data from the card schemes:

  • fingerprint: The fingerprint is a randomly generted identifer to cards that share the same number (PAN). If the PAN changed, Spreedly will generate a new identifer for the number. See our fingerprinting guide for more information.
  • card_type: Spreedly will detect and update the card_type field if the PAN changed. Note: if a payment method’s card_type changes, any stored credentials stored on that payment method are considered invalid and deleted. See our stored credentials guide for more information.
  • last_four_digits: Spreedly will update the last_four_digits of the credit card number if the PAN changed.
  • first_six_digits: Spreedly will update the first_six_digits of the credit card number if the PAN changed.

Email Notifications

We will send two e-mail notifications when your cards are updated. Notifications will be sent to all active users associated with the organization. The first email is notice that the card update process has started. Example:

The second email is notice that card updates have completed and contains aggregate information about the updates that were performed. Example:

Please visit the Help Center for a detailed walkthrough on pricing, opting-in, and opting-out.