Appearance
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
| Step | What Happens | Who Handles Sensitive Data |
|---|---|---|
| 1. Tokenize | Card number converted to token | Gateway (via iframe) |
| 2. Create Customer | Token exchanged for stored payment method | Gateway |
| 3. Process Payment | Customer ID used to charge card | Gateway |
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:
| Field | Value | Purpose |
|---|---|---|
data.id | d1hvstnsvrvefsmaknt0 | Customer ID — identifies the customer |
data.data.customer.defaults.payment_method_id | d1hvstnsvrvefsmaknsg | Payment 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
| Error | Cause | Solution |
|---|---|---|
Token expired | More than 2 minutes passed | Re-tokenize the card |
Token invalid | Token already used or malformed | Generate a new token |
Customer not found | Invalid customer ID | Verify ID in your database |
Payment method not found | Invalid payment method ID | List customer's payment methods |
Card declined | Issuer declined the transaction | Ask 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 Case | Documentation |
|---|---|
| Set up recurring subscriptions | Recurring Billing |
| Add multiple payment methods | Customer Vault |
| Process refunds | Refunds |
| Handle card updates automatically | Account Updater Triggers |
Quick Reference
Required IDs
| ID | Where to Get It | What It's For |
|---|---|---|
Public API Key (pub_XXX) | Control Panel → API Keys | Tokenizer (frontend) |
| Secret API Key | Control Panel → API Keys | All backend API calls |
| Customer ID | Response from Create Customer | Identify the customer |
| Payment Method ID | Response from Create Customer | Specify which card to charge |
Endpoints
| Endpoint | Method | Purpose |
|---|---|---|
/api/vault/customer | POST | Create customer with payment method |
/api/vault/customer/{id} | GET | Retrieve customer details |
/api/transaction | POST | Process a payment |