CardPointe Gateway - Production Ready

Date: October 9, 2025 Status: โœ… UAT Validation Complete - Awaiting Production Credentials


๐ŸŽ‰ Executive Summary

All Fiserv CardPointe validation requirements have been completed successfully. The integration is production-ready and awaiting production credentials from Fiserv.

What's Ready

โœ… All 10 validation retrefs generated and submitted โœ… Stored Credential Framework fully implemented (COF/cofscheduled) โœ… All 4 Fiserv compliance requirements met (postal, cvv2, ecomind, tokenization) โœ… PCI SAQ-A compliant (card data never touches our servers) โœ… Commerce Hub code removed (clean CardPointe-only implementation) โœ… UAT testing scripts created for future regression testing


๐Ÿ“‹ Validation Test Results

All 10 Required Retrefs Generated

Section 1: Card Not Present (4 retrefs)

Test Scenario Retref Status
Visa - $9.32 282494749400 โœ… Approved
Visa - $1,100.35 282244049402 โœ… Declined
American Express - $9.32 282496749403 โœ… Approved
American Express - $1,100.35 282245049404 โœ… Declined

Validates: postal, cvv2, ecomind, tokenization

Section 2: Customer Initiated Transactions (3 retrefs)

Test Scenario Retref Status
Token storage w/ transaction ($55.25) 282588750465 โœ… Success
Token storage, no transaction ($0) 282590750467 โœ… Success
Stored token usage, CIT ($100.25) 282591750468 โœ… Success

Validates: Customer-initiated stored credential framework (cof='C')

Section 3: Merchant Initiated Transactions (3 retrefs)

Test Scenario Retref Status
Token storage w/ transaction, for future recurring ($40.25) 282566150686 โœ… Success
Stored token usage, for recurring transaction ($26.50) 282354050700 โœ… Success
Stored token usage, for MIT ($150.00) 282609750702 โœ… Success

Validates: Merchant-initiated stored credential framework (cof='M', cofscheduled='Y/N')


๐Ÿ”ง Technical Implementation

Architecture Overview

Production Flow:
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 1. Customer enters card into CardPointe HIT iframe         โ”‚
โ”‚    (https://fts.cardconnect.com/itoke/ajax-tokenizer.html) โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                            โ†“
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 2. CardPointe tokenizes card (SAQ-A compliant)             โ”‚
โ”‚    Returns token + expiry via postMessage                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                            โ†“
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 3. Our backend receives token (never sees raw card)        โ”‚
โ”‚    POST /cardconnect/rest/auth                              โ”‚
โ”‚    - account: token                                         โ”‚
โ”‚    - ecomind: "E" (e-commerce)                             โ”‚
โ”‚    - postal: ZIP (for AVS/fraud prevention)                โ”‚
โ”‚    - cvv2: included in token                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                            โ†“
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 4. CardPointe processes authorization                       โ”‚
โ”‚    Returns retref + payment token for future use            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                            โ†“
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ 5. We save PaymentProfile + PaymentTransaction             โ”‚
โ”‚    Order marked as "pending"                                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Files Modified/Created

Core Adapter (Cleaned)

UAT Testing Scripts (New)

To re-run: source .kamal/secrets && bin/rails runner lib/scripts/fiserv_validation_test.rb

Frontend Integration

Controller


๐Ÿš€ Production Deployment Checklist

Step 1: Receive Production Credentials from Fiserv

Expected from Ryan McDonnell (integrationdelivery@fiserv.com):

Step 2: Update Environment Variables

File: .kamal/secrets

# Change from UAT:
export CARDPOINTE_API_URL="https://fts-uat.cardconnect.com"
export CARDPOINTE_MERCHANT_ID="490000000069"
export CARDPOINTE_API_USERNAME="testing"
export CARDPOINTE_API_PASSWORD="testing123"

# To Production:
export CARDPOINTE_API_URL="https://fts.cardconnect.com"
export CARDPOINTE_MERCHANT_ID="<PRODUCTION_MID_FROM_FISERV>"
export CARDPOINTE_API_USERNAME="<PRODUCTION_USERNAME>"
export CARDPOINTE_API_PASSWORD="<PRODUCTION_PASSWORD>"

Step 3: Update Frontend Iframe URL

File: app/views/app/quotes/payment.html.erb

Line 194 - Change iframe source:

// FROM (UAT):
iframe.src = 'https://fts-uat.cardconnect.com/itoke/ajax-tokenizer.html'

// TO (Production):
iframe.src = 'https://fts.cardconnect.com/itoke/ajax-tokenizer.html'

Line 212 - Change origin validation:

// FROM (UAT):
if (!/fts-uat\.cardconnect\.com$/.test(new URL(event.origin).host)) {

// TO (Production):
if (!/fts\.cardconnect\.com$/.test(new URL(event.origin).host)) {

Step 4: Deploy to Production

source .env && \
export KAMAL_REGISTRY_PASSWORD && \
export RAILS_MASTER_KEY && \
export POSTGRES_PASSWORD && \
kamal deploy

Step 5: Production Smoke Tests

Test a real transaction in production:

  1. Create a test order with a small amount (e.g., $1.00)
  2. Use a real card (or test card if Fiserv provides production test cards)
  3. Complete checkout flow
  4. Verify:
  5. Test capture flow when order is delivered
  6. Test void/refund flows if needed

๐Ÿ” Security & Compliance

PCI DSS Compliance

Stored Credential Framework Compliance

Per Visa/Mastercard mandate (effective October 2022):

Customer-Initiated Transactions (CIT):

Merchant-Initiated Transactions (MIT):

Implementation in adapter:

# app/services/payment_processors/fiserv_adapter.rb
def authorize_with_card_data_uat_only(
  cof: nil,           # "C" or "M"
  cofscheduled: nil,  # "Y" or "N"
  # ... other params
)
  payload[:cof] = cof if cof.present?
  payload[:cofscheduled] = cofscheduled if cofscheduled.present?
end

๐Ÿ“Š Database Schema

PaymentProfile

- processor              # 'fiserv' (for CardPointe)
- processor_customer_id  # Local ID: 'cardpointe_<MID>_<type>_<id>'
- processor_token_id     # CardPointe token (e.g., '9418594164541111')
- card_type              # Visa, Mastercard, Amex, etc.
- last_four              # Last 4 digits of card
- expiration_month       # 1-12
- expiration_year        # Full year (e.g., 2027)
- company_id             # NULLABLE - polymorphic
- contact_id             # NULLABLE - polymorphic
- is_default             # Boolean

PaymentTransaction

- processor                # 'fiserv'
- processor_transaction_id # CardPointe retref
- transaction_type         # 'authorization', 'capture', 'refund', 'void'
- status                   # 'approved', 'declined', 'pending'
- amount                   # Decimal
- response_code            # CardPointe respcode
- response_text            # Human-readable message
- metadata (jsonb)         # Extra processor data

๐Ÿงช Testing in Production

Test Cards (if provided by Fiserv)

Confirm with Fiserv if production test cards are available. In UAT:

Amount-Driven Responses (UAT only)

These work in UAT but NOT production:

In production, use real transactions or Fiserv-provided test cards.


๐Ÿ› Troubleshooting

Common Issues

401 Unauthorized

Cause: Wrong credentials Fix: Verify CARDPOINTE_API_USERNAME and CARDPOINTE_API_PASSWORD in .kamal/secrets

Token not received from iframe

Cause: Wrong iframe URL or origin mismatch Fix: Check iframe URL matches environment (UAT vs Production)

"Payment declined: Non-numeric expiry"

Cause: Expiry not passed from frontend Fix: Verify cardpointe_expiry hidden field is populated

"Invalid merchant"

Cause: Wrong merchant ID Fix: Verify CARDPOINTE_MERCHANT_ID matches Fiserv-provided MID


๐Ÿ“ž Support Contacts

Fiserv Integration Team

CardPointe Documentation


โœ… Production Readiness Checklist

Pre-Deployment

Post-Deployment

Optional


๐ŸŽฏ Next Actions

  1. Wait for Fiserv production approval (in progress)
  2. Receive production credentials from Ryan McDonnell
  3. Update environment variables in .kamal/secrets
  4. Update iframe URLs in payment.html.erb
  5. Deploy to production via Kamal
  6. Run smoke tests with small real transactions
  7. Monitor production for first few days

๐Ÿ“ Change Log

October 9, 2025

October 8, 2025

October 6, 2025


Status: Ready for production deployment pending Fiserv credentials Last Updated: October 9, 2025