Date: October 9, 2025 Status: โ UAT Validation Complete - Awaiting Production Credentials
All Fiserv CardPointe validation requirements have been completed successfully. The integration is production-ready and awaiting production credentials from Fiserv.
โ 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
| 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
| 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')
| 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')
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" โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
app/services/payment_processors/fiserv_adapter.rb
cof, cofscheduled)authorize_with_token, capture_authorization, void_authorization, refund_transactionlib/scripts/fiserv_validation_test.rb - Card Not Present testslib/scripts/fiserv_cit_validation_test.rb - Customer-Initiated testslib/scripts/fiserv_mit_validation_test.rb - Merchant-Initiated testsTo re-run: source .kamal/secrets && bin/rails runner lib/scripts/fiserv_validation_test.rb
app/views/app/quotes/payment.html.erb
app/controllers/app/quotes_controller.rb
FiservAdapter for CardPointe paymentsExpected from Ryan McDonnell (integrationdelivery@fiserv.com):
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>"
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)) {
source .env && \
export KAMAL_REGISTRY_PASSWORD && \
export RAILS_MASTER_KEY && \
export POSTGRES_PASSWORD && \
kamal deploy
Test a real transaction in production:
Per Visa/Mastercard mandate (effective October 2022):
Customer-Initiated Transactions (CIT):
cof: "C" parameterMerchant-Initiated Transactions (MIT):
cof: "M" parametercofscheduled: "Y" for recurring, "N" for one-timeImplementation 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
- 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
- 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
Confirm with Fiserv if production test cards are available. In UAT:
These work in UAT but NOT production:
In production, use real transactions or Fiserv-provided test cards.
Cause: Wrong credentials
Fix: Verify CARDPOINTE_API_USERNAME and CARDPOINTE_API_PASSWORD in .kamal/secrets
Cause: Wrong iframe URL or origin mismatch Fix: Check iframe URL matches environment (UAT vs Production)
Cause: Expiry not passed from frontend
Fix: Verify cardpointe_expiry hidden field is populated
Cause: Wrong merchant ID
Fix: Verify CARDPOINTE_MERCHANT_ID matches Fiserv-provided MID
.kamal/secrets updated with production credentialspayment.html.erb iframe URL updatedpayment.html.erb origin check updated.kamal/secretspayment.html.erbStatus: Ready for production deployment pending Fiserv credentials Last Updated: October 9, 2025