How to Securely Store Customer Payment Data
Which approach is right for your business?​
Securely storing customer payment data is essential for any SaaS business that handles recurring payments or wants to provide a seamless checkout experience. This guide will help you implement the right solution based on your specific business needs.
Key Use Cases:
- "I want to store just card numbers, not customer data" → Use Tokenization
- "I need to store all customer data in one place" → Use Profiling
- "I want to offer a 'save card for future purchases' feature" → Use Tokenization + the API
- "I need to avoid PCI compliance requirements" → Use Client-Side Tokenization
Your Situation | Recommended Approach | Why This Works |
---|---|---|
You have your own customer database | Tokenization | You keep control of customer data and only offload payment details |
You work with legacy systems or don't have a customer database | Profiling | Simpler integration with fewer infrastructure requirements |
You need flexibility in data queries | Tokenization | You maintain control over your data structure and queries |
You want to minimize PCI scope | Client-Side Tokenization | Payment data never touches your servers |
Try our examples yourself: Run in Postman
What is Tokenization and How Does It Help?​
Tokenization is a secure way to handle payment data that replaces sensitive card or bank account numbers with a non-sensitive equivalent (a "token") that can be safely stored in your system. This approach significantly reduces your PCI DSS compliance requirements while still allowing you to process recurring payments or offer one-click checkout. For complete details on all API parameters, see the Tokenization API Reference.
Before Tokenization: You store actual card numbers in your database, requiring full PCI DSS compliance, regular security audits, and significant infrastructure investment.
After Tokenization: You store only tokens in your database. The actual card data is securely stored by the payment gateway. This dramatically reduces your PCI scope and security responsibilities.
The UniPay tokenization system offers two main approaches:
Tokenization​
This approach converts only the payment card or bank account number into a token, while you continue to store other customer information (name, address, etc.) in your own system. This is ideal when you already have a customer database and just need to add secure payment storage.
Profiling​
This approach stores both the payment information and associated customer data (name, address, etc.) in the payment gateway. This is ideal when you don't have your own customer database or prefer to offload all customer data management.
Real-World Example:
SuperShop, an online store selling magazine subscriptions, needs to charge customers a monthly fee. Storing card numbers in their own database would require expensive PCI compliance. Instead, they use tokenization to securely store customer payment details while keeping other customer information in their existing database. They can now process recurring payments without handling sensitive card data.
Implementation Guide: Adding Secure Payment Storage​
Recipe: Adding a "Save card for future purchases" feature​
What you'll achieve: Allow customers to securely save their payment method for faster checkout on return visits.
Prerequisites:
- Active UniPay merchant account with API credentials
- Basic understanding of API integration
- Front-end capability to collect card information
- Familiarity with the Tokenization API Reference
Implementation steps:
-
Step 1: Choose the right tokenization approach Review the table above to determine whether basic tokenization or profiling is right for your needs.
-
Step 2: Set up your checkout form Add a checkbox that allows customers to opt in to saving their payment method.
-
Step 3: Implement tokenization When a customer checks the "save my card" box, make the tokenization API call along with the payment. See the Tokenization Request Parameters for details.
-
Step 4: Store the token in your database Associate the returned token with the customer's account in your system.
-
Step 5: Display saved payment methods Show masked card information (e.g., "Visa ending in 1234") on the checkout page for returning customers.
-
Step 6: Enable one-click checkout Allow customers to select a saved payment method and complete their purchase with minimal friction.
Try it yourself: Save Card Feature Collection
Recipe: Setting up secure payment storage without your own infrastructure​
What you'll achieve: Store customer payment data securely without maintaining your own customer database.
Prerequisites:
- Active UniPay merchant account with API credentials
- Basic understanding of API integration
- Familiarity with the Get-Profile API Reference
Implementation steps:
-
Step 1: Enable profiling for your account Contact support to ensure profiling is activated for your merchant account.
-
Step 2: Collect customer and payment information Design forms to capture both customer details and payment information.
-
Step 3: Submit for profiling Make the tokenization API call with all customer data fields populated. Refer to the Billing Address Information API Reference for all available fields.
-
Step 4: Store only the token ID Save the returned token in your system as a reference to the complete customer profile.
-
Step 5: Retrieve profile data when needed Use the get-profile API call to fetch customer information when needed.
Try it yourself: Profiling Integration Collection
Technical Implementation​
- Server-Side Tokenization
- Client-Side Tokenization
- Using Tokens
Server-side tokenization involves collecting payment information on your server and then sending it to the payment gateway for tokenization. This approach is simple but requires your server to briefly handle sensitive payment data. See the Server-Side Tokenization API Reference for complete parameter details.
- Java
- JavaScript
- Python
- PHP
- Ruby
- Go
// Create a token for a payment card
String url = "https://secure.unipay.com/gates/xurl?";
String data = "requestType=tokenization" +
"&userName=YOUR_USERNAME" +
"&password=YOUR_PASSWORD" +
"&accountId=YOUR_ACCOUNT_ID" +
"&accountType=R" +
"&accountNumber=4111111111111111" +
"&accountAccessory=0525" +
"&holderName=John Smith";
// Send request and get response
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setDoOutput(true);
try (OutputStream os = conn.getOutputStream()) {
os.write(data.getBytes());
}
// Parse response to get token
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String response = reader.lines().collect(Collectors.joining());
String token = extractTokenFromResponse(response);
// Save to your database (never store the actual card number!)
String customerId = "12345";
String sql = "UPDATE customers SET payment_token = ? WHERE id = ?";
preparedStatement.setString(1, token);
preparedStatement.setString(2, customerId);
preparedStatement.executeUpdate();
// Create a token for a payment card
const tokenizeCard = async () => {
const formData = new URLSearchParams();
formData.append('requestType', 'tokenization');
formData.append('userName', 'YOUR_USERNAME');
formData.append('password', 'YOUR_PASSWORD');
formData.append('accountId', 'YOUR_ACCOUNT_ID');
formData.append('accountType', 'R');
formData.append('accountNumber', '4111111111111111');
formData.append('accountAccessory', '0525');
formData.append('holderName', 'John Smith');
try {
const response = await fetch('https://secure.unipay.com/gates/xurl?', {
method: 'POST',
body: formData
});
const data = await response.text();
// Parse response to get token
const token = extractTokenFromResponse(data);
// Save to your database (never store the actual card number!)
const customerId = "12345";
await saveTokenToDatabase(customerId, token);
} catch (error) {
console.error('Error:', error);
}
};
tokenizeCard();
import requests
# Create a token for a payment card
url = "https://secure.unipay.com/gates/xurl?"
payload = {
"requestType": "tokenization",
"userName": "YOUR_USERNAME",
"password": "YOUR_PASSWORD",
"accountId": "YOUR_ACCOUNT_ID",
"accountType": "R",
"accountNumber": "4111111111111111",
"accountAccessory": "0525",
"holderName": "John Smith"
}
response = requests.post(url, data=payload)
response_data = response.text
# Parse response to get token
token = extract_token_from_response(response_data)
# Save to your database (never store the actual card number!)
customer_id = "12345"
import sqlite3
conn = sqlite3.connect('your_database.db')
cursor = conn.cursor()
cursor.execute("UPDATE customers SET payment_token = ? WHERE id = ?",
(token, customer_id))
conn.commit()
conn.close()
<?php
// Create a token for a payment card
$url = 'https://secure.unipay.com/gates/xurl?';
$data = array(
'requestType' => 'tokenization',
'userName' => 'YOUR_USERNAME',
'password' => 'YOUR_PASSWORD',
'accountId' => 'YOUR_ACCOUNT_ID',
'accountType' => 'R',
'accountNumber' => '4111111111111111',
'accountAccessory' => '0525',
'holderName' => 'John Smith'
);
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data)
)
);
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
// Parse response to get token
$token = extract_token_from_response($response);
// Save to your database (never store the actual card number!)
$customer_id = "12345";
$pdo = new PDO('mysql:host=localhost;dbname=your_database', 'username', 'password');
$stmt = $pdo->prepare("UPDATE customers SET payment_token = ? WHERE id = ?");
$stmt->execute([$token, $customer_id]);
?>
begin
# Create a token for a payment card
response = api.tokenization(
accountType: "R", # R for payment card
accountNumber: card_number,
accountAccessory: expiry_date, # Format: MMYY
holderName: customer_name,
# Include billing address for better verification
street: billing_street,
city: billing_city,
state: billing_state,
zipCode: billing_zip,
countryCode: billing_country,
# Optional verification to ensure the card is valid
verificationMode: "PF" # Processor verification
)
if response.responseCode == "A01"
# Success - save the token
save_token_to_database(customer_id, response.token)
return { success: true, message: "Card saved successfully" }
else
# Tokenization failed
log_error("Tokenization failed: #{response.responseMessage}")
return { success: false, message: "Unable to save card: #{response.responseMessage}" }
end
rescue StandardError => e
log_error("Tokenization error: #{e.message}")
return { success: false, message: "System error while saving card" }
end
package main
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
func main() {
// Create a token for a payment card
apiURL := "https://secure.unipay.com/gates/xurl?"
data := url.Values{}
data.Set("requestType", "tokenization")
data.Set("userName", "YOUR_USERNAME")
data.Set("password", "YOUR_PASSWORD")
data.Set("accountId", "YOUR_ACCOUNT_ID")
data.Set("accountType", "R")
data.Set("accountNumber", "4111111111111111")
data.Set("accountAccessory", "0525")
data.Set("holderName", "John Smith")
client := &http.Client{}
req, err := http.NewRequest("POST", apiURL, strings.NewReader(data.Encode()))
if err != nil {
panic(err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
// Parse response to get token
token := extractTokenFromResponse(string(body))
// Save to your database (never store the actual card number!)
customerId := "12345"
db, _ := sql.Open("sqlite3", "./your_database.db")
defer db.Close()
stmt, _ := db.Prepare("UPDATE customers SET payment_token = ? WHERE id = ?")
stmt.Exec(token, customerId)
}
Important Security Considerations:
- When using server-side tokenization, your servers will briefly handle sensitive card data, which increases your PCI compliance requirements.
- Consider using client-side tokenization instead to completely remove card data from your systems.
- Never log or store raw card numbers, even temporarily.
- See the Security Best Practices section in the API Reference.
Try the API: Server-Side Tokenization Collection
Client-side tokenization uses the proxynization.js library to create tokens directly from the customer's browser, without sensitive card data ever touching your servers. This approach significantly reduces your PCI scope. For implementation details, see the Client-Side Tokenization API Reference.
<!-- Include the proxynization.js library -->
<script src="https://secure.unipay.com/services/api.proxynization.js"></script>
<!-- Create your payment form -->
<form id="payment-form">
<input type="text" id="cardNumber" placeholder="Card Number">
<input type="text" id="expiryDate" placeholder="MM/YY">
<input type="text" id="holderName" placeholder="Cardholder Name">
<input type="hidden" id="token" name="token">
<button type="button" onclick="tokenizeCard()">Save Card</button>
</form>
<script>
// Set the temporary password obtained from your server
ProxynizationAPI.password = 'temporary-password-from-server';
// Define the callback function
function tokenCallback(responseCode, responseMessage, proxyNumber) {
if (responseCode == "A01") {
// Success - set the token in the hidden field
document.getElementById('token').value = proxyNumber;
// Submit the form to your server
document.getElementById('payment-form').submit();
} else {
// Handle errors
alert("Could not save card: " + responseMessage);
}
}
// Function to tokenize the card
function tokenizeCard() {
ProxynizationAPI.process('#cardNumber', 'tokenCallback');
}
</script>
// Endpoint to get temporary password
app.post('/api/get-temporary-password', async (req, res) => {
try {
// Make the authentication request to get a temporary password
const authResponse = await axios.get(
'https://secure.unipay.com/gates/xurl',
{
params: {
requestType: 'authentication',
userName: process.env.UNIPAY_USERNAME,
password: process.env.UNIPAY_PASSWORD,
contextType: 'proxynization'
}
}
);
// Check if the request was successful
if (authResponse.data && authResponse.data.temporaryPassword) {
return res.json({
success: true,
temporaryPassword: authResponse.data.temporaryPassword
});
} else {
console.error('Failed to get temporary password:', authResponse.data);
return res.json({
success: false,
message: 'Unable to get authorization'
});
}
} catch (error) {
console.error('Error getting temporary password:', error);
return res.json({
success: false,
message: 'System error'
});
}
});
Best Practice: Client-side tokenization is strongly recommended as it completely removes card data from your systems, significantly reducing your PCI compliance burden. Your servers will never see the actual card numbers, only the tokens. For implementation details, see the Proxynization API Reference.
Try the API: Client-Side Tokenization Collection
Once you have a token stored, you can use it to process payments without needing the original card data. This makes recurring billing and one-click checkout features simple and secure. See the Token Payment API Reference for all required parameters.
- Java
- JavaScript
- Python
- PHP
- Ruby
- Go
// Process a payment using a stored token
Map<String, Object> request = new HashMap<>();
request.put("accountType", "R"); // R for payment card
request.put("token", "tkn_1234567890abcdef"); // The stored token
request.put("accountAccessory", "0525"); // Expiration date still required
request.put("amount", 1999); // Amount in cents ($19.99)
Response response = api.sale(request);
// Process a payment using a stored token
const processPaymentWithToken = async () => {
const paymentData = {
accountType: 'R', // R for payment card
token: 'tkn_1234567890abcdef', // The stored token
accountAccessory: '0525', // Expiration date still required
amount: 1999 // Amount in cents ($19.99)
};
try {
const response = await fetch('https://secure.unipay.com/gates/xurl?', {
method: 'POST',
body: new URLSearchParams({
requestType: 'sale',
userName: 'YOUR_USERNAME',
password: 'YOUR_PASSWORD',
...paymentData
})
});
const data = await response.text();
return processResponse(data);
} catch (error) {
console.error('Error:', error);
}
};
# Process a payment using a stored token
url = "https://secure.unipay.com/gates/xurl?"
payload = {
"requestType": "sale",
"userName": "YOUR_USERNAME",
"password": "YOUR_PASSWORD",
"accountType": "R", # R for payment card
"token": "tkn_1234567890abcdef", # The stored token
"accountAccessory": "0525", # Expiration date still required
"amount": 1999 # Amount in cents ($19.99)
}
response = requests.post(url, data=payload)
response_data = response.text
<?php
// Process a payment using a stored token
$url = 'https://secure.unipay.com/gates/xurl?';
$data = array(
'requestType' => 'sale',
'userName' => 'YOUR_USERNAME',
'password' => 'YOUR_PASSWORD',
'accountType' => 'R', // R for payment card
'token' => 'tkn_1234567890abcdef', // The stored token
'accountAccessory' => '0525', // Expiration date still required
'amount' => 1999 // Amount in cents ($19.99)
);
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data)
)
);
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
?>
# Process a payment using a stored token
response = api.sale(
accountType: "R", # R for payment card
token: "tkn_1234567890abcdef", # The stored token
accountAccessory: "0525", # Expiration date still required
amount: 1999 # Amount in cents ($19.99)
)
// Process a payment using a stored token
apiURL := "https://secure.unipay.com/gates/xurl?"
data := url.Values{}
data.Set("requestType", "sale")
data.Set("userName", "YOUR_USERNAME")
data.Set("password", "YOUR_PASSWORD")
data.Set("accountType", "R") // R for payment card
data.Set("token", "tkn_1234567890abcdef") // The stored token
data.Set("accountAccessory", "0525") // Expiration date still required
data.Set("amount", "1999") // Amount in cents ($19.99)
client := &http.Client{}
req, err := http.NewRequest("POST", apiURL, strings.NewReader(data.Encode()))
if err != nil {
panic(err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
- Java
- JavaScript
- PHP
- Ruby
- Go
public Map<String, Object> processPaymentWithToken(String customerId, int amountInCents, String description) {
// Retrieve token and expiry date from your database
Map<String, Object> customer = null;
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(
"SELECT payment_token, payment_expiry, name FROM customers WHERE id = ?")) {
stmt.setString(1, customerId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
customer = new HashMap<>();
customer.put("payment_token", rs.getString("payment_token"));
customer.put("payment_expiry", rs.getString("payment_expiry"));
customer.put("name", rs.getString("name"));
}
}
} catch (SQLException e) {
logError("Database error: " + e.getMessage());
return Map.of("success", false, "message", "System error retrieving payment method");
}
if (customer == null || customer.get("payment_token") == null || customer.get("payment_expiry") == null) {
return Map.of("success", false, "message", "No payment method found");
}
try {
// Process the payment using the token
Map<String, Object> saleRequest = new HashMap<>();
saleRequest.put("accountType", "R");
saleRequest.put("token", customer.get("payment_token"));
saleRequest.put("accountAccessory", customer.get("payment_expiry"));
saleRequest.put("amount", amountInCents);
saleRequest.put("transactionIndustryType", "EC"); // EC for ecommerce
saleRequest.put("holderName", customer.get("name"));
saleRequest.put("transactionCode", "ORDER-" + System.currentTimeMillis()); // Your order reference
Response response = api.sale(saleRequest);
if ("A01".equals(response.getResponseCode())) {
// Success - payment approved
saveTransactionRecord(
customerId,
amountInCents,
description,
response.getTransactionId(),
"approved",
null
);
Map<String, Object> result = new HashMap<>();
result.put("success", true);
result.put("message", "Payment successful");
result.put("transaction_id", response.getTransactionId());
return result;
} else {
// Payment declined
logError("Payment declined: " + response.getResponseMessage());
saveTransactionRecord(
customerId,
amountInCents,
description,
response.getTransactionId(),
"declined",
response.getResponseMessage()
);
Map<String, Object> result = new HashMap<>();
result.put("success", false);
result.put("message", "Payment declined: " + response.getResponseMessage());
return result;
}
} catch (Exception e) {
logError("Payment processing error: " + e.getMessage());
return Map.of("success", false, "message", "System error processing payment");
}
}
async function processPaymentWithToken(customerId, amountInCents, description) {
try {
// Retrieve token and expiry date from your database
const customer = await db.query(
'SELECT payment_token, payment_expiry, name FROM customers WHERE id = ?',
[customerId]
).then(results => results[0]);
if (!customer || !customer.payment_token || !customer.payment_expiry) {
return { success: false, message: "No payment method found" };
}
try {
// Process the payment using the token
const response = await api.sale({
accountType: 'R',
token: customer.payment_token,
accountAccessory: customer.payment_expiry,
amount: amountInCents,
transactionIndustryType: 'EC', // EC for ecommerce
holderName: customer.name,
transactionCode: `ORDER-${Date.now()}` // Your order reference
});
if (response.responseCode === 'A01') {
// Success - payment approved
await saveTransactionRecord({
customer_id: customerId,
amount: amountInCents,
description: description,
transaction_id: response.transactionId,
status: 'approved'
});
return {
success: true,
message: "Payment successful",
transaction_id: response.transactionId
};
} else {
// Payment declined
console.error(`Payment declined: ${response.responseMessage}`);
await saveTransactionRecord({
customer_id: customerId,
amount: amountInCents,
description: description,
transaction_id: response.transactionId,
status: 'declined',
decline_reason: response.responseMessage
});
return {
success: false,
message: `Payment declined: ${response.responseMessage}`
};
}
} catch (error) {
console.error(`Payment processing error: ${error.message}`);
return { success: false, message: "System error processing payment" };
}
} catch (error) {
console.error(`Database error: ${error.message}`);
return { success: false, message: "System error retrieving payment method" };
}
}
</TabItem>
<TabItem value="python" label="Python">
```python showLineNumbers
# Process a payment using a stored token
url = "https://secure.unipay.com/gates/xurl?"
payload = {
"requestType": "sale",
"userName": "YOUR_USERNAME",
"password": "YOUR_PASSWORD",
"accountType": "R", # R for payment card
"token": "tkn_1234567890abcdef", # The stored token
"accountAccessory": "0525", # Expiration date still required
"amount": 1999 # Amount in cents ($19.99)
}
response = requests.post(url, data=payload)
response_data = response.text
<?php
// Process a payment using a stored token
$url = 'https://secure.unipay.com/gates/xurl?';
$data = array(
'requestType' => 'sale',
'userName' => 'YOUR_USERNAME',
'password' => 'YOUR_PASSWORD',
'accountType' => 'R', // R for payment card
'token' => 'tkn_1234567890abcdef', // The stored token
'accountAccessory' => '0525', // Expiration date still required
'amount' => 1999 // Amount in cents ($19.99)
);
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data)
)
);
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
?>
# Process a payment using a stored token
response = api.sale(
accountType: "R", # R for payment card
token: "tkn_1234567890abcdef", # The stored token
accountAccessory: "0525", # Expiration date still required
amount: 1999 # Amount in cents ($19.99)
)
// Process a payment using a stored token
apiURL := "https://secure.unipay.com/gates/xurl?"
data := url.Values{}
data.Set("requestType", "sale")
data.Set("userName", "YOUR_USERNAME")
data.Set("password", "YOUR_PASSWORD")
data.Set("accountType", "R") // R for payment card
data.Set("token", "tkn_1234567890abcdef") // The stored token
data.Set("accountAccessory", "0628") // Updated expiration date
client := &http.Client{}
req, err := http.NewRequest("POST", apiURL, strings.NewReader(data.Encode()))
if err != nil {
panic(err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
Try the API: Retokenization Collection
Untokenization​
Processors charge fees for every stored token. For this reason, it is not cost-effective to keep tokens associated with cards/accounts of customers who no longer perform transactions. The gateway provides the ability to remove unused tokens. For complete parameter details, see the Untokenization API Reference.
# Remove a token that is no longer needed
response = api.untokenization(
token: "tkn_1234567890abcdef"
)
Note: For PCI compliance reasons, detokenization (retrieving the original card number from a token) typically requires contacting payment gateway support.
Try the API: Untokenization Collection
Business Benefits​
🔒 Enhanced Security Reduce your risk exposure by storing tokens instead of actual card data. Even in the event of a data breach, tokens are useless to attackers.
💰 Reduced Compliance Costs Significantly reduce your PCI DSS compliance scope and associated costs. With client-side tokenization, you can potentially qualify for the simplest SAQ A form.
🔄 Simplified Repeat Purchases Enable one-click checkout experiences that boost conversion rates. Customers with saved payment methods are more likely to complete purchases.
📈 Higher Retention Make it easier to implement subscription billing, reducing customer churn and creating predictable revenue streams.
Getting Started Checklist​
-
Review API Reference Familiarize yourself with all available endpoints and parameters in the Tokenization API Reference.
-
Ensure tokenization is enabled Contact support to confirm your account has tokenization functionality enabled.
-
Choose your approach Decide between basic tokenization and profiling based on your business needs.
-
Select implementation method Choose between server-side or client-side tokenization based on your PCI requirements.
-
Design database schema Create a database structure to securely store and associate tokens with customer records.
-
Implement and test Implement the API calls in your development environment and thoroughly test before deploying.
Implementation Timeline​
Stage | Duration | Potential Delays |
---|---|---|
API account setup and verification | 1-2 days | Approval processes, incorrect documentation |
Basic tokenization implementation | 2-5 days | Technical issues, resources availability |
Client-side tokenization setup | 3-7 days | Frontend integration complexities, CORS issues |
Testing and bug fixing | 3-5 days | Edge cases, integration issues with existing systems |
Production deployment | 1-2 days | Deployment window availability, unexpected issues |
Need Technical Support? If you encounter issues during implementation, our Developer Support team is available to help. You can also visit our Developer Forum to connect with other integrators. For complete API specifications, refer to the Tokenization API Reference.
Important:
- Even when using a token, you must still include the
accountAccessory
field (expiration date for cards or routing number for bank accounts). See the Token Sale Parameters for details.- Tokens are permanent unless explicitly deleted with an untokenization request.
- If a card expires, you can update the expiration date without creating a new token using the retokenization API call.
Try the API: Token Payment Collection
Problem-Solving Guide​
If you encounter an issue with tokenization​
Problem: Token not being created
Common causes:
- Incorrect API credentials
- Invalid card number format
- Tokenization not enabled for your account
Solution:
- Verify your API credentials
- Check card number format and validation
- Contact support to ensure tokenization is enabled
- See the Response Codes Reference for detailed error handling
Problem: Authorization errors with client-side tokenization
Common causes:
- Incorrect contextType parameter
- Temporary password expired
- CORS issues with API requests
Solution:
- Ensure contextType is set to "proxynization"
- Remember temporary passwords are valid for only 10 minutes
- Verify your domain is whitelisted for CORS
Problem: Errors when using tokens for transactions
Common causes:
- Missing or incorrect accountAccessory (expiration date/routing number)
- Token has been deleted or is invalid
- The token belongs to a different merchant account
Solution:
- Always include the correct accountAccessory field with tokens
- Verify the token exists and is correct
- Ensure you're using the correct accountId for the token
Without Technical Knowledge​
If you don't have the technical resources to implement tokenization directly, consider these alternatives:
- Pre-built plugins: Many popular e-commerce and CMS platforms have pre-built plugins for UniPay that include tokenization functionality.
- Hosted Payment Pages: UniPay offers hosted payment pages that handle all the tokenization for you, with no coding required.
- Integration partners: Contact one of our certified integration partners who can handle the implementation for you.
Advanced Tokenization Features​
Retokenization​
Some processors provide the ability to update token data when a customer's payment information changes (such as an updated expiration date). For this purpose, retokenization is used. For full parameter details, see the Retokenization API Reference.
- Java
- JavaScript
- Python
- PHP
- Ruby
- Go
// Update the expiration date for an existing token
Map<String, Object> retokenRequest = new HashMap<>();
retokenRequest.put("token", "tkn_1234567890abcdef");
retokenRequest.put("accountAccessory", "0628"); // Updated expiration date
Response response = api.retokenization(retokenRequest);
// Update the expiration date for an existing token
const updateTokenExpiry = async () => {
try {
const response = await fetch('https://secure.unipay.com/gates/xurl?', {
method: 'POST',
body: new URLSearchParams({
requestType: 'retokenization',
userName: 'YOUR_USERNAME',
password: 'YOUR_PASSWORD',
token: 'tkn_1234567890abcdef',
accountAccessory: '0628' // Updated expiration date
})
});
const data = await response.text();
return processResponse(data);
} catch (error) {
console.error('Error:', error);
}
};
# Update the expiration date for an existing token
url = "https://secure.unipay.com/gates/xurl?"
payload = {
"requestType": "retokenization",
"userName": "YOUR_USERNAME",
"password": "YOUR_PASSWORD",
"token": "tkn_1234567890abcdef",
"accountAccessory": "0628" # Updated expiration date
}
response = requests.post(url, data=payload)
response_data = response.text
<?php
// Update the expiration date for an existing token
$url = 'https://secure.unipay.com/gates/xurl?';
$data = array(
'requestType' => 'retokenization',
'userName' => 'YOUR_USERNAME',
'password' => 'YOUR_PASSWORD',
'token' => 'tkn_1234567890abcdef',
'accountAccessory' => '0628' // Updated expiration date
);
$options = array(
'http' => array(
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'method' => 'POST',
'content' => http_build_query($data)
)
);
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
?>
# Update the expiration date for an existing token
response = api.retokenization(
token: "tkn_1234567890abcdef",
accountAccessory: "0628" # Updated expiration date
)
// Update the expiration date for an existing token
apiURL := "https://secure.unipay.com/gates/xurl?"
data := url.Values{}
data.Set("requestType", "retokenization")
data.Set("userName", "YOUR_USERNAME")
data.Set("password", "YOUR_PASSWORD")
data.Set("token", "tkn_1234567890abcdef")
data.Set("accountAccessory", "0628") // Updated expiration date
client := &http.Client{}
req, err := http.NewRequest("POST", apiURL, strings.NewReader(data.Encode()))
if err != nil {
panic(err)
}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
Try the API: Retokenization Collection
Untokenization​
Processors charge fees for every stored token. For this reason, it is not cost-effective to keep tokens associated with cards/accounts of customers who no longer perform transactions. The gateway provides the ability to remove unused tokens. For complete parameter details, see the Untokenization API Reference.
- Java
- JavaScript
- Python
- PHP
- Ruby
- Go
// Remove a token that is no longer needed
ApiClient api = new ApiClient();
Response response = api.untokenization("tkn_1234567890abcdef");
// Remove a token that is no longer needed
const response = await api.untokenization({
token: "tkn_1234567890abcdef"
});
# Remove a token that is no longer needed
response = api.untokenization(
token="tkn_1234567890abcdef"
)
// Remove a token that is no longer needed
$response = $api->untokenization([
"token" => "tkn_1234567890abcdef"
]);
# Remove a token that is no longer needed
response = api.untokenization(
token: "tkn_1234567890abcdef"
)
// Remove a token that is no longer needed
response, err := api.Untokenization("tkn_1234567890abcdef")
if err != nil {
log.Fatal(err)
}
Note: For PCI compliance reasons, detokenization (retrieving the original card number from a token) typically requires contacting payment gateway support.
Try the API: Untokenization Collection
Business Benefits​
🔒 Enhanced Security Reduce your risk exposure by storing tokens instead of actual card data. Even in the event of a data breach, tokens are useless to attackers.
💰 Reduced Compliance Costs Significantly reduce your PCI DSS compliance scope and associated costs. With client-side tokenization, you can potentially qualify for the simplest SAQ A form.
🔄 Simplified Repeat Purchases Enable one-click checkout experiences that boost conversion rates. Customers with saved payment methods are more likely to complete purchases.
📈 Higher Retention Make it easier to implement subscription billing, reducing customer churn and creating predictable revenue streams.
Getting Started Checklist​
-
Review API Reference Familiarize yourself with all available endpoints and parameters in the Tokenization API Reference.
-
Ensure tokenization is enabled Contact support to confirm your account has tokenization functionality enabled.
-
Choose your approach Decide between basic tokenization and profiling based on your business needs.
-
Select implementation method Choose between server-side or client-side tokenization based on your PCI requirements.
-
Design database schema Create a database structure to securely store and associate tokens with customer records.
-
Implement and test Implement the API calls in your development environment and thoroughly test before deploying.
Implementation Timeline​
Stage | Duration | Potential Delays |
---|---|---|
API account setup and verification | 1-2 days | Approval processes, incorrect documentation |
Basic tokenization implementation | 2-5 days | Technical issues, resources availability |
Client-side tokenization setup | 3-7 days | Frontend integration complexities, CORS issues |
Testing and bug fixing | 3-5 days | Edge cases, integration issues with existing systems |
Production deployment | 1-2 days | Deployment window availability, unexpected issues |
Need Technical Support? If you encounter issues during implementation, our Developer Support team is available to help. You can also visit our Developer Forum to connect with other integrators. For complete API specifications, refer to the Tokenization API Reference.