Skip to main content

Storing Card and Bank Data

Introduction

This document outlines the process for storing payment data outside the flow of a normal payment. If you are collecting payment data as part of a checkout flow, please follow the guide Accepting Payments. To facilitate this flow we leverage a resource called a Payment Method Intent (PMI). The PMI feature allows partners to securely store card details for future use, optionally validate them via a authorization call to the network, and efficiently manage fees associated with the process (if any).


Feature Overview

What is a Payment Method Intent (PMI)?

A Payment Method Intent (PMI) is a dedicated resource created solely for storing payment method details with the purpose of using them at a later time.

Features of the PMI process:

  • Optional Validation: The card can be validated using the network auth process to verify the card’s legitimacy without an actual charge. CVV and AVS checks can also be made.

How to use Payment Method Intents

There are two primary ways to create and use a Payment Method Intent (PMI), depending on whether you have the payment method data available upfront or need to collect it later. Below are the two approaches:

  1. Pre-Setup Without Payment Method Data This method is ideal for scenarios where payment details are collected later, such as in a UI-driven checkout process.
  • How It Works: You create a PMI by specifying the payment method type (e.g., card or bank) without providing the actual payment method details (e.g., card number or bank account info).
  • Outcome: The system generates a PMI object with a client_secret. This client_secret is a temporary token that you pass to your frontend to collect the payment details in a secure way. Once the details are provided, the client_secret is used to finalize the creation of the payment method.
  • Intended Use: This flow is intended to be used when you want to collect payment data from a customer but do not wish to charge then until later (ex. monthly subscription).
  1. Direct PMI Creation With Payment Method Data This method is suited for situations where you already have the payment method details and want to store them immediately, such as during a bulk token conversion.
  • How It Works: You create a PMI by including the full payment method data (e.g., card number, expiration date, etc.) in the initial request.
  • Outcome: The PMI is created, and the payment method is stored directly in the system. No client_secret is generated since the data is provided upfront.
  • Intended Use: This is ideal for efficiently converting a large number of payment methods into the system (e.g., migrating from another provider). You can process multiple PMIs at once without requiring additional steps.

Optional Validation: In both methods, you can enable validation by setting the validate: true parameter. This triggers a $0 authorization to confirm the payment method’s legitimacy. For bank accounts, validation is always performed and will error if invalid. If validation fails, the PMI status updates accordingly, and in bulk updates, the payment method is not created.


Billing and fees

The Payment Method Intent (PMI) feature includes options for managing fees associated with the validation process, giving partners flexibility in how costs are allocated. When a PMI is created with the optional $0 authorization validation (using validate: true), fees may be incurred:

Partners have control over who is responsible for covering validation fees through the bill_to parameter in the PMI creation request. There are two options:

  • Bill to Partner - the partner pays the pre-negotiated validation fee
  • Bill to Merchant (Default) - The merchant pay the transaction fee based on their processing plan. Card brand is factored in when determining this fee.

Example:

{
"payment_method_types": "card",
"validate": true,
"bill_to": "partner"
}

Validating Cards outside the Create Process

In addition to validating payment methods during the creation of a Payment Method Intent (PMI), partners can validate existing cards or bank accounts outside the PMI creation flow using the $0 authorization feature. This process allows you to verify the legitimacy of a stored payment method without initiating an actual charge, making it useful for periodic checks or pre-transaction validation.

Using the $0 Auth Feature

The $0 auth feature involves creating a new Payment Intent (PI) with an amount of $0 and the capture parameter set to false. This triggers an authorization request to the payment network to validate the payment method without capturing funds. The process generates new PI and Payment Method (PM) objects, which are used solely for validation and cannot be repurposed for actual transactions.

How It Works

  • Setup: Use an existing payment method (e.g., a card or bank account already stored in the system) to create a new Payment Intent.
  • Authorization: Set the amount to $0 to perform a validation-only authorization. This checks the payment method’s validity, including optional CVV and AVS verification for cards.
  • Outcome: If successful, the payment method is confirmed as valid. The resulting PI and PM objects remain in an "uncaptured" state and are not reusable for charging.

Code Examples for Payment Method Intent (PMI) Flows

  • Flow 1: Pre-Setup Without Payment Method Data (With Client Secret):

When to Use

  • UI-Driven Checkouts: Use this when you’re building a customer-facing interface (e.g., a web or mobile app) where users enter their card or bank details during a checkout or account setup process.
  • Deferred Collection: Perfect for cases where you want to pre-create the PMI and finalize it later, such as allowing users to add a payment method at their convenience.
  • Flexibility: Offers a two-step process, separating PMI creation from payment method attachment.

Example Request:

curl -X POST https://<domain from rep>/payment_method_intents \
-H 'Content-Type: application/json' \
-H 'x-api-key: key_123456789' \
-H 'x-account-id: acct_123' \
-d '{
"payment_method_types": ["card"],
"validate": true,
"bill_to": "merchant",
}'

Response:

{
"id": "pmi_2vV2scu...",
"entity": "payment_method_intent",
"status": "created",
"account_id": "acct_2gR2ynO...",
"created_at": "2025-04-09T16:28:48.787Z",
"updated_at": "2025-04-09T16:28:48.787Z",
"client_secret": "pmi_2vV2scu..._secret_2vV2sbY...",
"payment_method_types": [
"card"
],
"validate": true,
"bill_to": "merchant"
}
  • Flow 2: Direct PMI Creation With Payment Method Data (Without Client Secret):

When to Use

  • Bulk Updates: Use this when migrating or converting a large number of existing payment methods from another system into your platform (e.g., transitioning merchants to Forward).
  • Backend-Driven Processes: Ideal for server-side operations where payment method data is already available, such as importing data from a partner’s database.
  • Efficiency: Best when you want to create and store payment methods in one API call without additional steps.

Example Request:

curl -X POST https://<domain from rep>/payment_method_intents \
-H 'Content-Type: application/json' \
-H 'x-account-id: acct_asdfsd' \
-H 'x-api-key: key_123456789' \
-d '{
"payment_method_types": ["card"],
"payment_method_data": {
"persist_on_validation_failure": false,
"payment_method_type" : "card",
"source_type": "cardpointe",
"card": {
"last_four_digits": "4242",
"first_six_digits": "424242",
"brand": "visa",
"exp_month": "08",
"exp_year": "29",
"token": "token_foo"
}
},
"validate" : true,
"bill_to": "merchant"
}'

Expected Response (Success), Assuming the $0 auth validation succeeds:

{
"id": "pmi_2vV31CW...",
"entity": "payment_method_intent",
"status": "succeeded",
"account_id": "acct_2gR2yn...",
"created_at": "2025-04-09T16:29:56.451Z",
"updated_at": "2025-04-09T16:29:56.451Z",
"payment_method_types": [
"card"
],
"payment_method": {
"id": "pm_2vV319...",
"entity": "payment_method",
"created_at": "2025-04-09T16:29:56.339Z",
"updated_at": "2025-04-09T16:29:56.339Z",
"payment_method_type": "card",
"source_type": "cardpointe",
"card": {
"last_four_digits": "4242",
"brand": "visa",
"first_six_digits": "424242",
"exp_month": "08",
"exp_year": "2029"
}
},
"validate": true,
"bill_to": "merchant",
"latest_validation_response": {
"status": "succeeded",
"message": "Success",
"created_at": "2025-04-09T16:29:56.339Z"
},
"validation_attempts": [
{
"response": {
"status": "succeeded",
"message": "Success",
"created_at": "2025-04-09T16:29:56.339Z"
},
"payment_method": {
"payment_method_type": "card",
"source_type": "cardpointe",
"card": {
"last_four_digits": "4242",
"first_six_digits": "424242",
"brand": "visa",
"exp_month": "08",
"exp_year": "2029",
"token": "token_foo"
}
}
}
]
}

Expected Response (Failure), If validation fails after 5 attempts (e.g., due to an invalid card), the status becomes failed:

{
"id": "pmi_2vV3GTG...",
"entity": "payment_method_intent",
"status": "failed",
"account_id": "acct_2gR2ynO...",
"created_at": "2025-04-09T16:31:57.749Z",
"updated_at": "2025-04-09T16:31:57.749Z",
"client_secret": "pmi_2vV3GTGF..._secret_2vV3GNd...",
"payment_method_types": [
"card"
],
"validate": true,
"bill_to": "merchant",
"latest_validation_response": {
"status": "failed",
"message": "Gateway not found",
"created_at": "2025-04-09T16:31:57.749Z"
},
"validation_attempts": [
{
"response": {
"status": "failed",
"message": "Gateway not found",
"created_at": "2025-04-09T16:31:57.749Z"
},
"payment_method": {
"payment_method_type": "card",
"source_type": "cardpointe",
"card": {
"last_four_digits": "4242",
"first_six_digits": "424242",
"brand": "visa",
"exp_month": "08",
"exp_year": "2029",
"token": "token_foo"
}
}
}
]
}