Set up Express Checkout
Follow these steps to set up Affirm Express Checkout, including checkout configuration, shipping options, and total calculations.
Interested in offering Affirm Express Checkout?
Reach out to your Affirm Account Manager to confirm availability and for next steps on building your APIs.
The Express Checkout Integration Checklist can be used to track your progress
Integration Steps
1. Create a Unique Order Identifier
Generate a unique order identifier, such as a cart, session, or order ID, within your backend. We recommend using a high-entropy identifier (e.g., a UUID) rather than a sequential number to ensure security. This ID serves as the primary reference for the customer's session throughout the Affirm checkout process and will be returned to your server in Steps 3 and 4, enabling you to securely identify the transaction and calculate the appropriate shipping options and order totals.
2. Configure Affirm Express Checkout
To initiate the Express Checkout flow, you must configure the Affirm checkout object with the following specific requirements:
- Set the checkout type: Include
checkout_variant: "express"in themerchantobject. - Specify the Callback URL: Include the
shipping_and_totals_callback_urlstring within the merchant object. This defines the server-to-server endpoint Affirm will call in real time to fetch shipping methods, taxes, and updated totals once the customer inputs their address. - Set the order ID: Include the
order_idfield with the unique identifier generated in Step 1. This ID will be sent back to your server in the Shipping & Totals callback request (Step 3) and returned in the Read Checkout API response (Step 4), allowing you to associate the Affirm checkout with the correct cart session. - Set the subtotal: Provide a
subtotalinteger representing the cost of the items before taxes and shipping. This value must be set within themetadataobject. Checkouts with a subtotal less than your configured cart floor are not eligible for the Express Checkout flow. - Omit Shipping and Total: Do not include the
shippingobject ortotalfield, as these will be determined during the Affirm-hosted flow. - Include standard attributes: Ensure the
itemsarray and all other standard checkout attributes are included.
affirm.checkout({
merchant: {
checkout_variant: "express", // ensure this attribute is passed for Express Checkout
shipping_and_totals_callback_url: "https://merchantsite.com/shipping-totals?example_param=123456ABC", // Affirm will call this endpoint server-to-server to get shipping options and totals based on the customer's shipping address
user_confirmation_url: "https://merchantsite.com/confirm",
user_cancel_url: "https://merchantsite.com/cancel",
public_api_key: "YOUR_PUBLIC_KEY",
user_confirmation_url_action: "POST",
},
order_id: "unique_merchant_cart_identifier", // merchant's unique order identifier
metadata: {
subtotal: 20000, // cost of items excluding taxes or shipping
// rest of metadata attributes
},
items: [{
display_name: "Awesome Pants",
sku: "ABC-123",
unit_price: 10000,
qty: 2,
}],
// exclude the shipping object and the total field
// rest of checkout object attributes
});
affirm.checkout.open();3. Set-up Shipping & Totals HTTP endpoint
This step collects the customer shipping information and checkout totals omitted during Affirm Express Checkout configuration. Setup a HTTP endpoint that accepts the merchant order identifier and customer shipping address as input and returns all valid shipping options with shipping amount, tax amount, and total order amount. The URL for this endpoint is the one that is passed into the shipping_and_totals_callback_url field during Affirm checkout creation..
The user will then select a shipping option, and the corresponding total order amount for that selection will be used to underwrite the Affirm loan. This final amount is then returned via the transaction authorization endpoint for your verification.
Endpoint Security
- Request Verification (HMAC): To ensure that requests to your endpoint are legitimately from Affirm, you must verify the
X-Affirm-Signatureheader.- The Signature: Affirm generates an HMAC-SHA512 hash of the current timestamp and the request body using your Private API Key. The header value will be formatted as
“t={current_timestamp},v0={hash("{current_timestamp}.{request_body}")}” - Verification: Re-calculate the hash on your server using your Private Key and compare it to the header value. If they do not match, the request should be rejected as unauthorized (HTTP 401). If the
current_timestampis older than 5 minutes the request should also be rejected.current_timestampwill be formatted as the time in seconds since the epoch as an integer (ex. 1772576438). - Key Rotation: During periods of key rotation Affirm will generate a hash value with each of the two keys and format the header value as
“t={current_timestamp},v0={key1_hash}={key2_hash}”where key1 is the older of the two keys
- The Signature: Affirm generates an HMAC-SHA512 hash of the current timestamp and the request body using your Private API Key. The header value will be formatted as
- Callback URL Validation: To prevent malicious callback URLs from being injected during checkout creation, Affirm validates the
shipping_and_totals_callback_urlhostname against a list of expected hostname patterns registered for your merchant account. Only the hostname is validated; the URL path, path parameters, and query parameters are not checked. Affirm supports and encourages different allowlists per environment e.g., sandbox and production. To register or update your allowed hostname patterns, contact your Technical Account Manager (TAM) as these allowlists are configured internally by Affirm and are not directly accessible or editable by merchants. - Secure Communications: All communication between Affirm and your server-side endpoints must be conducted over HTTPS using TLS 1.2 or higher.
- Perimeter Security: If your production environment uses a CDN, Firewall, or other network security layer, you must allowlist Affirm's outbound IP addresses. Because Affirm uses real-time server-to-server requests to fetch shipping options and totals, these security layers may block this traffic and return a 403 error. Please reach out to your Technical Account Manager (TAM) to get the required list of production and sandbox IP addresses.
Endpoint performance
- Response Time (SLA): Affirm requires that your Shipping & Totals HTTP Endpoint responds within 5,000 milliseconds (5 seconds).
- Timeout behavior: If your endpoint fails to respond within this window, the Affirm hosted checkout will encounter a timeout error and the customer will be unable to complete their purchase.
Handling Calculation & Validation Errors
If your backend is unable to calculate shipping options or order totals due to restricted shipping zones, invalid address data, or inventory shortages, you must return an HTTP 422 Unprocessable Entity status code.
When this occurs, Affirm expects to receive a response body containing the property errors with an array of objects containing the following:
error_code: Affirm parses this machine-readable string to determine the internal logic and flow handling.message: This string is displayed directly to the customer within the Affirm checkout interface to explain why the checkout cannot proceed.fields: (Optional) An array of strings identifying the specific request payload properties that caused the error (e.g., ["shipping_address.country"]).
For detailed schema requirements and example payloads, please refer to the Express Checkout API page.
Example
On user shipping address selection, Affirm will send a request with a payload similar to the following:
{
"currency":"USD",
// The merchant order id provided by the merchant
"order_id": "unique_merchant_cart_identifier",
"shipping":{
"line1": "123 Example Street",
"line2": "Apt 123",
"city": "San Francisco",
"country": "USA",
"state": "CA",
"zipcode": "94107"
}
}If the request is successful, Affirm will expect a HTTP 200 response similar to the following:
{
"order_id": "unique_merchant_cart_identifier",
"currency": "USD",
"subtotal": 20000,
"shipping_options": [
{
"shipping_type": "unique_merchant_shipping_identifier_1",
"shipping_label": "Standard Shipping (7-10 Days)",
"shipping_amount": 0,
"tax_amount": 100,
"total": 20100
},
{
"shipping_type": "unique_merchant_shipping_identifier_2",
"shipping_label": "Express Shipping (3-5 Days)",
"shipping_amount": 200,
"tax_amount": 100,
"total": 20300
},
{
"shipping_type": "unique_merchant_shipping_identifier_3",
"shipping_label": "Overnight Shipping (1 Day)",
"shipping_amount": 500,
"tax_amount": 100,
"total": 20600
}
]
}If the request is unsuccessful, the endpoint should return a 422 Unprocessable Entity status code. The response body must contain an errors array with one or more error objects:
Single error
{
"errors": [
{
"error_code": "UNSUPPORTED_SHIPPING_ZONE",
"message": "We currently do not offer shipping to Hawaii or Alaska.",
"fields": [
"shipping_address.state"
]
}
]
}Multiple errors
{
"errors": [
{
"error_code": "INVALID_SHIPPING_ADDRESS",
"message": "The provided ZIP code does not match the selected state.",
"fields": [
"shipping_address.zipcode",
"shipping_address.state"
]
},
{
"error_code": "INVENTORY_UNAVAILABLE",
"message": "One or more items in your cart are no longer in stock."
}
]
}4. Calculate Order Total
After the customer’s Affirm checkout is complete, retrieve the finalized order details from Affirm and verify the total before completing the transaction.
Retrieve checkout details
Use the checkout_id to call the Read Checkout API:
GET /api/v2/checkout/{checkout_id}This returns the full shipping and customer details including:
- Shipping address
- Selected shipping method
- Final shipping, tax, and total amounts
- Customer's name
- Customer's email
- Customer's phone number
Use this data to calculate the final order total in your backend.
Order ID
If your system generates a new order ID at this stage that differs from the initial ID provided in Step 1, ensure you pass this updated identifier when authorizing the transaction.
From this point on, use the updated order ID as the primary reference for all subsequent Transaction API calls.
Validate the total
Call the Authorize Transaction endpoint to retrieve the authorized loan amount from Affirm.
Compare the following:
- Your calculated total.
- Affirm’s authorized amount.
These values must match exactly to ensure the legally authorized loan covers the final order cost before proceeding.
Finalize the order
If the totals match:
- Complete the transaction.
- Route the customer to your receipt page.
If they do not match:
- Do not fulfill the order.
- Treat as a validation failure.
Sample Read Checkout API response
{
"order_id": "unique_merchant_cart_identifier",
"currency": "USD",
// order totals collected during user checkout journey
"total": 20600,
"tax_amount": 100,
"shipping_amount": 500,
"shipping": {
// shipping method collected during user checkout journey
"shipping_type": "unique_merchant_shipping_identifier_3",
"shipping_label": "Standard Shipping (7-10 Days)",
"address": {
"city": "San Francisco",
"country": "USA",
"line1": "123 Example Street",
"line2": "Apt 123",
"state": "CA",
"zipcode": "94107"
}
// name, phone_number, and email will only be provided within billing
},
"billing": {
"name": {
"last": "string",
"first": "string"
},
"phone_number": "1234567890",
"email": "[email protected]"
// address will only be provided within shipping
},
"merchant": {
"checkout_variant": "express", // flag for whether this checkout was Express
"public_api_key": "string",
"user_cancel_url": "string",
"user_confirmation_url": "string",
"user_confirmation_url_action": "string",
"name": "string",
},
// rest of checkout data
"metadata": {
"subtotal": 20000 // subtotal
},
"billing_frequency": "monthly",
"financial_program_external_name": "string",
"financial_program_name": "standard_3_6_12",
"loan_type": "classic",
"financing_program": "string",
"merchant_external_reference": "ab-12345",
"mfp_rule_input_data": {
"items": {
"sku_number": {
"sku": 0,
"item_url": "string",
"display_name": "string",
"unit_price": 0,
"qty": 0,
"item_type": "string",
"item_image_url": "string"
}
},
"total": 49999,
"metadata": {
"checkout_channel_type": "online",
"mode": "redirect"
},
"financing_program": "string"
},
"checkout_type": "merchant",
"checkout_flow_type": "classic",
"checkout_status": "string",
"use_adaptive": true,
"config": "string",
"product_type": "string",
"api_version": "v2",
"product": "string",
"suppress_expiration_declination_messaging": true,
"meta": {
"release": "string",
"user_timezone": "America/Los_Angeles",
"_affirm_tracking_uuid": "356a483a-86b2-4846-b6f2-70d37d95a78c"
}
}5. Add Affirm Checkout Button
Add the pre-styled Affirm Checkout button on your website for Express Checkout. See Checkout Button for details.
Example:
<div class="affirm-checkout-button-container"
data-page-type="product"
data-size="large"
data-theme="dark"
data-shape="rounded"
data-button-text="checkout"
>
</div>What’s next?
Updated 5 days ago
USA