Skip to content

Tokenizer → Customer → Payment Workflow

Securely collect payment information, store it for future use, and process transactions—all while staying out of PCI scope.

Overview

This workflow is ideal when you need to:

  • Save cards for future purchases — Returning customers can pay with one click
  • Set up recurring billing — Subscriptions, memberships, installment plans
  • Enable account-based purchasing — B2B customers with payment on file

How It Works

Summary

StepWhat HappensWho Handles Sensitive Data
1. TokenizeCard number converted to tokenGateway (via iframe)
2. Create CustomerToken exchanged for stored payment methodGateway
3. Process PaymentCustomer ID used to charge cardGateway

Your servers never touch raw card data—only tokens and IDs.


Step 1: Tokenize

Use the Tokenizer to securely collect card information from your customer. The card data goes directly to the gateway via an iframe and never touches your servers.

Frontend

html
<div id="payment-form"></div>
<button onclick="submitPayment()">Save Payment Method</button>

<script src="https://sandbox.gosparrowone.com/tokenizer/tokenizer.js"></script>
<script>
const tokenizer = new Tokenizer({
  apikey: 'pub_XXXXXXXXXXXXXX',  // Your PUBLIC key
  container: '#payment-form',
  submission: (response) => {
    if (response.status === 'success') {
      // Send token to your backend
      createCustomer(response.token)
    } else {
      console.error('Tokenization failed:', response.msg)
    }
  }
})

function submitPayment() {
  tokenizer.submit()
}

function createCustomer(token) {
  fetch('/api/create-customer', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ token: token })
  })
  .then(response => response.json())
  .then(data => {
    console.log('Customer created:', data.customerId)
  })
}
</script>

Response

json
{
  "status": "success",
  "token": "Z2a87b7wk9PHzwl2RZLvslq1UzjzjHl1"
}

Token Expiration

Tokens expire after 2 minutes. Send the token to your backend and create the customer record immediately.

See Tokenizer Documentation for complete configuration options.


Step 2: Create Customer

From your backend server, use the token to create a customer in the vault. This stores the payment method securely for future use.

Request

Response

json
{
  "status": "success",
  "msg": "success",
  "data": {
    "id": "d1hvstnsvrvefsmaknt0",  // ← Save this Customer ID
    "owner_id": "testmerchant12345678",
    "data": {
      "customer": {
        "description": "John Smith - Premium Member",
        "addresses": [
          {
            "id": "d1hvstnsvrvefsmakns0",
            "first_name": "John",
            "last_name": "Smith",
            "city": "Chicago",
            "state": "IL",
            "postal_code": "60601"
          }
        ],
        "defaults": {
          "billing_address_id": "d1hvstnsvrvefsmakns0",
          "payment_method_id": "d1hvstnsvrvefsmaknsg",  // ← Save this Payment Method ID
          "payment_method_type": "card",
          "shipping_address_id": ""
        }
      }
    },
    "created_at": "2025-07-01T10:27:50.541931-05:00",
    "updated_at": "2025-07-01T10:27:50.541932-05:00"
  }
}

What to Store

Save these IDs in your database, linked to your user record:

FieldValuePurpose
data.idd1hvstnsvrvefsmaknt0Customer ID — identifies the customer
data.data.customer.defaults.payment_method_idd1hvstnsvrvefsmaknsgPayment Method ID — identifies the specific card

Multiple Payment Methods

A customer can have multiple payment methods. Use the payment_method_id to specify which one to charge, or omit it to use the default.

See Customer Vault API Reference for more options.


Step 3: Process

Now you can charge the customer using the stored IDs—no card data needed.

Default Payment

Specific Payment

If the customer has multiple cards on file, specify which one:

Response

json
{
  "status": "success",
  "msg": "success",
  "data": {
    "id": "txn_abc123xyz",
    "type": "sale",
    "amount": 2500,
    "amount_authorized": 2500,
    "amount_captured": 2500,
    "response": "approved",
    "response_code": 100,
    "customer_id": "d1hvstnsvrvefsmaknt0",
    "response_body": {
      "card": {
        "card_type": "visa",
        "masked_card": "411111******1111",
        "auth_code": "TAS123"
      }
    },
    "created_at": "2025-07-01T10:30:00.000000-05:00"
  }
}

See Transaction API Reference for all transaction options.


Backend Example

Here's a full Node.js example showing the entire flow:

javascript
const express = require('express')
const app = express()

const API_KEY = process.env.GATEWAY_API_KEY  // Your SECRET key
const API_URL = 'https://sandbox.gosparrowone.com'

// Step 2: Create customer from token
app.post('/api/create-customer', async (req, res) => {
  const { token, email, firstName, lastName } = req.body

  const response = await fetch(`${API_URL}/api/vault/customer`, {
    method: 'POST',
    headers: {
      'Authorization': API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      description: `${firstName} ${lastName}`,
      default_payment: { token },
      default_billing_address: {
        first_name: firstName,
        last_name: lastName,
        email: email
      }
    })
  })

  const data = await response.json()

  if (data.status === 'success') {
    // Save to your database
    await saveCustomerToDb({
      email: email,
      gatewayCustomerId: data.data.id,
      paymentMethodId: data.data.data.customer.defaults.payment_method_id
    })

    res.json({ success: true, customerId: data.data.id })
  } else {
    res.status(400).json({ success: false, error: data.msg })
  }
})

// Step 3: Charge the customer
app.post('/api/charge-customer', async (req, res) => {
  const { customerId, amount } = req.body

  const response = await fetch(`${API_URL}/api/transaction`, {
    method: 'POST',
    headers: {
      'Authorization': API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      type: 'sale',
      amount: amount,  // Amount in cents
      payment_method: {
        customer: { id: customerId }
      }
    })
  })

  const data = await response.json()

  if (data.status === 'success' && data.data.response === 'approved') {
    res.json({ 
      success: true, 
      transactionId: data.data.id,
      authCode: data.data.response_body.card.auth_code
    })
  } else {
    res.status(400).json({ 
      success: false, 
      error: data.data?.response || data.msg 
    })
  }
})

Error Handling

Common Errors

ErrorCauseSolution
Token expiredMore than 2 minutes passedRe-tokenize the card
Token invalidToken already used or malformedGenerate a new token
Customer not foundInvalid customer IDVerify ID in your database
Payment method not foundInvalid payment method IDList customer's payment methods
Card declinedIssuer declined the transactionAsk customer for different card

Declines

javascript
const response = await chargeCustomer(customerId, amount)

if (response.data.response === 'declined') {
  // Notify customer
  await sendEmail(customerEmail, {
    subject: 'Payment Failed',
    body: `Your payment of $${amount/100} was declined. Please update your payment method.`
  })
  
  // Log for review
  console.log('Decline reason:', response.data.response_body.card.processor_response_text)
}

Next Steps

Now that you have customers with stored payment methods, you can:

Use CaseDocumentation
Set up recurring subscriptionsRecurring Billing
Add multiple payment methodsCustomer Vault
Process refundsRefunds
Handle card updates automaticallyAccount Updater Triggers

Quick Reference

Required IDs

IDWhere to Get ItWhat It's For
Public API Key (pub_XXX)Control Panel → API KeysTokenizer (frontend)
Secret API KeyControl Panel → API KeysAll backend API calls
Customer IDResponse from Create CustomerIdentify the customer
Payment Method IDResponse from Create CustomerSpecify which card to charge

Endpoints

EndpointMethodPurpose
/api/vault/customerPOSTCreate customer with payment method
/api/vault/customer/{id}GETRetrieve customer details
/api/transactionPOSTProcess a payment

Last updated: