Affirm-developers

Status Visibility

Overview

When your customers go through the Affirm checkout flow, you can see their status in real-time via the Affirm API. We send a webhook request to your server when a customer:

  • Enters checkout
  • Receives a credit decision
  • Confirms their loan

Usage

You can use this information for customer service purposes, such as phone support, or to identify potential customers for email marketing campaigns. You can also use Status Visibility for in-checkout session (push) notifications and feedback. This guide covers how to receive Status Visibility updates from Affirm.

Checkout events

User experienceAffirm eventWebhook event
Selects Affirm as payment option and is sent to AffirmCheckout createdopened
Creates or signs into an Affirm accountnonenone
Processing ScreenApproval decision approved
not_approved
* more_information_needed
Selects loan terms and agrees to disclosuresnonenone
Clicks confirm and is sent back to merchant siteCheckout confirmationconfirmed

Event definitions

Customers must opt-in for the approval decisions to be included. The confirmed event is the only webhook event that is guaranteed to be present for each successful Affirm checkout attempt. All other events (approved/not approved/etc.) will only be present if the customer has opted into the Affirm data sharing policy. Customers are actively prompted to enable data sharing, and they can toggle this setting ("Personalized Services") in their Affirm user profile at any time (https://www.affirm.com/u/#/settings).

Opened

When the customer has opened the checkout link and sees the account sign-in/sign-up screen. This is the first step of the Affirm checkout flow.

Approved

The customer has been approved for their Affirm purchase.

Not Approved

The customer has been declined credit for some reason. The reason is purposely ambiguous and may be due to credit-worthiness, inability to verify identity, or insufficient financial records.

More Information Needed

The customer was required to provide additional information to determine credit-worthiness or identity and exited the checkout without providing the requested information.

Confirmed

The customer has confirmed the loan. This is the last step of the Affirm checkout flow, which should subsequently trigger charge authorization.

Integration

In order to receive Status Visibility updates from Affirm:

Affirm will send an HTTP POST request to a URL that you provide to your Client Success Manager at Affirm. That URL should be a page or endpoint that you've set up to receive requests from our servers.

Endpoint requirements:

  • HTTP/HTTPS
  • Receives POST method
  • Returns 20x status
  • Support TLS 1.2 (self-signed or expired certificates are not accepted)
  • Rate dependent on merchant checkout traffic
  • Inbound IP is dynamic
  • Content-Type: application/x-www-form-urlencoded
  • Accept-Encoding: gzip

Webhook Request Properties:

  • POST method
  • User-Agent: Affirm-Webhook
  • Origin IP is dynamic
  • Number of fields varies; is based on:
    • Event type - approved/declined/more_information events have conditional fields
    • User opt-in - dictates whether conditional fields are present

Respond to a webhook event:

Your endpoint should respond to Affirm with a 2xx status code to communicate that you successfully received the webhook event notification. Affirm will treat all other response types as a failed delivery, including 3xx status codes. This means that we will treat URL redirections and "Not Modified" responses as failures. Affirm will ignore any other information returned in the request headers or request body.

Affirm doesn't currently resend a webhook event if your endpoint didn't successfully receive it.

Security Considerations:

Authentication

We can support Basic Authentication in the URL. You'll need to provide your endpoint URL username and password to your Client Success Manager.

Example: AB123:<[email protected]>/affirm_webhook

Signed Requests

Affirm signs webhook requests so you can optionally verify that Affirm is sending the request instead of a third-party pretending to be Affirm. Each request includes a base-64 encoded header, X-Affirm-Signature. The value of the header is an HMAC-SHA256 hash signature computed from the request payload and your private API key found in your merchant dashboard.

Replay attack prevention

If a third-party intercepts a request payload and its signature, your endpoint is susceptible to a replay attack. To mitigate these attacks, we include a timestamp in the X-Affirm-Signature header. Since the timestamp is part of the signed payload, the attacker cannot change the timestamp without invalidating the signature. If the signature is valid but the timestamp is old, you should reject the request. We recommend rejecting a response with a timestamp 5 minutes older than the current time.

Checkout webhook request body

Content-Type: application/x-www-form-urlencoded

checkout_token=I97HK0EREM38YHK3
created=2019-02-27T22%3A50%3A52.601851
event=confirmed
event_timestamp=2019-02-27T22%3A51%3A57.941799
order_id=000000017
webhook_session_id=A1b2C3
checkout_tokenstringWhen the user exits, cancels, or is declined in the Affirm checkout flow
eventenum opened
approved
not_approved
more_information_needed
* confirmed
event_timestamptimestampUTC (YYYY-MM-DDTHH:MM:SS:SSSSSS)
createdtimestampUTC (YYYY-MM-DDTHH:MM:SS:SSSSSS)
order_idstring*(Optional) Must be enabled by Affirm. Only included if merchant passes the value in the checkout object
webhook_session_idstring*(Optional) Must be enabled by Affirm. Only included if merchant passes the value in the checkout object

🚧

The order_id and webhook_session_id fields are unavailable in Shopify.

Reconciliation

You can reconcile with your own customer records based on any of these fields:

  • Checkout_token - provided by Affirm
  • Order ID - provided by the Merchant
  • Webhook Session ID - provided by the Merchant

🚧

The order_id and webhook_session_id fields are unavailable in Shopify. Please reach out to your Client Success Manager at Affirm. They can provide access to the customer email address to be received in the webhook for reconciliation.

If using an Affirm-provided field, such as the checkout_token, you would need to store this ID in your system to later reconcile against incoming webhook requests.

If using one of your known fields (order_id, customer email, webhook_session_id), you should be able to reconcile the incoming webhook requests with the information you have on-hand.

The order_id and webhook_session_id sent in the checkout object is in all incoming webhook requests for that checkout attempt. You can reconcile this data with your customer records.

Example code

//-------------------------
// START AFFIRM MFP EXAMPLE

    var toInteger = function(a) {
    var _a;
    if (typeof a === "number" && a != Math.floor(a)){
        return a * 100;
    }
    if (typeof a === "number" && a === Math.floor(a)) {
        return a;
    }
    if (typeof a === "string" && a.includes('.')) {
        var _a = a.replace(/[$\.\,]/gi,'');
        return parseInt(_a);
    }
    if (typeof a === "string" && a.includes('$')) {
        var _a = a.replace(/[$\,]/gi,'') + '00';
        return parseInt(_a);
    }
    else {
        return parseInt(a);
    }
}

    var affirm_mfp_config = {
        // Price threshold
        "min": 30000,
        
        // Put your MFP Promo ID below:
        "promo_id": "",
        // Put your MFP Financing Program name below:
        "program_name": "",

        "default_promo_id": "",
        "default_program_name": ""
    };
    
    // Your existing price element's ID (optional)
    var affirm_price_elid = "the-price";
    
    // A delay to let things load (optional)
    var affirm_trigger = "refresh-button";
    
    if(affirm_trigger){
        document.getElementById(affirm_trigger);
        affirm_trigger.addEventListener('click',function(){affirmRefresh(affirm_current_price)});
    }
        var affirm_program_name = "";
      var affirm_current_price = function() {
        var _current_price;
        var _affirm_promos = document.getElementsByClassName('affirm-as-low-as');

       var _price_el = document.getElementById(affirm_price_elid);
       if (!_price_el) {
          console.log("No price provided");
          return false
        }
        if (_price_el.tagName === "INPUT"){
          _current_price = toInteger(_price_el.value);
        }
        else {
          _current_price = toInteger(_price_el.innerText);
        }
      return _current_price
      }

    var affirm_mfp_eligible = function(a) {
        return affirm_current_price >= affirm_mfp_config.min
    }
  
  function affirmRefresh(a) {
    var _affirm_promos = document.getElementsByClassName('affirm-as-low-as');
    for (var i = _affirm_promos.length - 1; i >= 0; i--) {
      _affirm_promos[i].setAttribute('data-amount',affirm_current_price());
    }
    affirmMfp(a);
    affirm.ui.refresh();
  }
  
    function affirmMfp(a) {
        var _affirm_promos = document.getElementsByClassName('affirm-as-low-as');
        if (affirm_mfp_eligible(a)) {
            console.log('mfp');
            for (var i = _affirm_promos.length - 1; i >= 0; i--) {
                _affirm_promos[i].setAttribute('data-promo-id',affirm_mfp_config.promo_id);
            }
            affirm_program_name = affirm_mfp_config.program_name;
        }
        else {
            for (var i = _affirm_promos.length - 1; i >= 0; i--) {
            console.log('default');
            _affirm_promos[i].setAttribute('data-promo-id',affirm_mfp_config.default_promo_id);
            }
            affirm_program_name = affirm_mfp_config.default_program_name
        }
    }

    // affirm_checkout_object = {
    // ...
    //    "financing_program_name" : affirm_program_name;
    // ...
    // }
  
 // END AFFIRM MFP EXAMPLE
 //-------------------------

Updated 6 months ago


Status Visibility


Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.