Payment Tokens

Stripe Payment Tokens with Vrio API - Integration Guide

Overview

This guide explains how to use Stripe.js with Elements to generate payment tokens and send them to Vrio's API for payment processing. The Vrio platform supports modern confirmation tokens, traditional payment method tokens, and legacy source tokens.

Supported Token Types

Vrio's Stripe gateway supports the following token types:

1. Confirmation Tokens (Recommended)

  • Format: ctoken_xxxxxxxxxxxx
  • Benefits: Enhanced security, better 3D Secure support, future-proof, allows two-step confirmation

2. Payment Method Tokens

  • Format: pm_xxxxxxxxxxxx
  • Benefits: Widely supported, established pattern, better UX than legacy tokens

3. Source Tokens (Legacy)

  • Format: src_xxxxxxxxxxxx or tok_xxxxxxxxxxxx
  • Note: Still supported but not recommended for new integrations

Frontend Implementation with Stripe.js

For detailed implementation examples and code samples, please refer to Stripe's official documentation:

Confirmation Tokens (Recommended)

Payment Method Tokens

Legacy Source Tokens

For backward compatibility, you can still use legacy card tokens:

Implementation Example

Here's a complete working example of how to implement Stripe payment tokens with Vrio:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Stripe Payment Tokens Example</title>
    <script src="https://js.stripe.com/v3/"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 600px;
            margin: 0 auto;
            padding: 20px;
        }
        .form-group {
            margin-bottom: 20px;
        }
        label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
        }
        input, select {
            width: 100%;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-sizing: border-box;
        }
        #payment-element, #payment-method-element, #card-element {
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 4px;
            background-color: white;
        }
        button {
            background-color: #007cba;
            color: white;
            padding: 12px 20px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 16px;
            margin-bottom: 10px;
        }
        button:disabled {
            background-color: #ccc;
            cursor: not-allowed;
        }
        .error {
            color: #e74c3c;
            margin-top: 5px;
        }
        .success {
            color: #27ae60;
            margin-top: 5px;
        }
        .info {
            background-color: #e8f4fd;
            border: 1px solid #bee5eb;
            border-radius: 4px;
            padding: 10px;
            margin-bottom: 20px;
        }
        .section {
            border: 1px solid #ddd;
            padding: 20px;
            margin-bottom: 20px;
            border-radius: 4px;
        }
        .section h3 {
            margin-top: 0;
        }
    </style>
</head>
<body>
<h1>Stripe Payment Tokens Example</h1>

<div class="info">
    <strong>Note:</strong> This example demonstrates creating different types of Stripe payment tokens for use with the Vrio API.
</div>

<div class="section">
    <h3>Configuration</h3>
    <div class="form-group">
        <label for="publishable-key">Stripe Publishable Key:</label>
        <input type="text" id="publishable-key" placeholder="pk_test_..." required>
    </div>

    <div class="form-group">
        <label for="token-type">Token Type:</label>
        <select id="token-type" onchange="handleTokenTypeChange()">
            <option value="confirmation">Confirmation Token (Recommended)</option>
            <option value="payment-method">Payment Method Token</option>
            <option value="legacy">Legacy Token</option>
        </select>
    </div>

    <button onclick="initializeStripe()">Initialize Stripe</button>
    <div id="init-status"></div>
</div>

<div class="section">
    <h3>Customer Information</h3>
    <div class="form-group">
        <label for="cardholder-name">Cardholder Name:</label>
        <input type="text" id="cardholder-name" value="John Doe" required>
    </div>

    <div class="form-group">
        <label for="email">Email:</label>
        <input type="email" id="email" value="[email protected]" required>
    </div>
</div>

<div class="section">
    <h3>Payment Information</h3>
    <div id="payment-element-container" style="display: none;">
        <label>Payment Information (Payment Element):</label>
        <div id="payment-element"></div>
        <div id="payment-errors" class="error"></div>
    </div>

    <div id="payment-method-element-container" style="display: none;">
        <label>Payment Information (Payment Element):</label>
        <div id="payment-method-element"></div>
        <div id="payment-method-errors" class="error"></div>
    </div>

    <div id="card-element-container" style="display: none;">
        <label>Card Information (Legacy Card Element):</label>
        <div id="card-element"></div>
        <div id="card-errors" class="error"></div>
    </div>
</div>

<div class="section">
    <h3>Create Token</h3>
    <button id="create-token-button" onclick="createToken()" disabled>Create Token</button>
    <div id="token-result"></div>
</div>

<script>
    let stripe;
    let elements;
    let paymentElement;
    let paymentMethodElements;
    let paymentMethodElement;
    let cardElement;
    let isInitialized = false;

    function initializeStripe() {
        const publishableKey = document.getElementById('publishable-key').value.trim();
        const tokenType = document.getElementById('token-type').value;
        const statusDiv = document.getElementById('init-status');

        if (!publishableKey) {
            statusDiv.innerHTML = '<div class="error">Please enter a Stripe publishable key</div>';
            return;
        }

        try {
            // Initialize Stripe
            stripe = Stripe(publishableKey);

            // Clear previous elements
            if (paymentElement) paymentElement.unmount();
            if (paymentMethodElement) paymentMethodElement.unmount();
            if (cardElement) cardElement.unmount();

            // Hide all containers
            document.getElementById('payment-element-container').style.display = 'none';
            document.getElementById('payment-method-element-container').style.display = 'none';
            document.getElementById('card-element-container').style.display = 'none';

            if (tokenType === 'confirmation') {
                // Create Payment Element for confirmation tokens (card only)
                elements = stripe.elements({
                    mode: 'payment',
                    amount: 2000, // Amount in cents
                    currency: 'usd',
                    payment_method_types: ['card'], // Restrict to card only
                    setup_future_usage: 'off_session', // Match what Vrio expects
                    capture_method: 'manual', // This might help reduce Link options
                });

                paymentElement = elements.create('payment', {
                    layout: 'tabs',
                    defaultValues: {
                        billingDetails: {
                            name: '',
                            email: '',
                        }
                    },
                    wallets: {
                        applePay: 'never',
                        googlePay: 'never'
                    },
                    terms: {
                        card: 'never'
                    },
                    style: {
                        base: {
                            fontSize: '16px',
                            color: '#424770',
                            '::placeholder': {
                                color: '#aab7c4',
                            },
                        },
                    },
                });

                paymentElement.mount('#payment-element');
                document.getElementById('payment-element-container').style.display = 'block';

            } else if (tokenType === 'payment-method') {
                // Create Payment Element for payment method tokens
                paymentMethodElements = stripe.elements({
                    mode: 'setup',
                    currency: 'usd',
                    payment_method_types: ['card'],
                    setup_future_usage: 'off_session',
                    paymentMethodCreation: 'manual',
                });

                paymentMethodElement = paymentMethodElements.create('payment', {
                    layout: 'tabs',
                    defaultValues: {
                        billingDetails: {
                            name: '',
                            email: '',
                        }
                    },
                    wallets: {
                        applePay: 'never',
                        googlePay: 'never'
                    },
                    terms: {
                        card: 'never'
                    },
                    style: {
                        base: {
                            fontSize: '16px',
                            color: '#424770',
                            '::placeholder': {
                                color: '#aab7c4',
                            },
                        },
                    },
                });

                paymentMethodElement.mount('#payment-method-element');
                document.getElementById('payment-method-element-container').style.display = 'block';

            } else {
                // Create legacy Card Element for true tok_ tokens
                elements = stripe.elements();

                cardElement = elements.create('card', {
                    style: {
                        base: {
                            fontSize: '16px',
                            color: '#424770',
                            '::placeholder': {
                                color: '#aab7c4',
                            },
                        },
                        invalid: {
                            color: '#e74c3c',
                        },
                    },
                });

                cardElement.mount('#card-element');
                document.getElementById('card-element-container').style.display = 'block';

                // Listen for real-time validation errors from the card Element
                cardElement.on('change', ({error}) => {
                    const displayError = document.getElementById('card-errors');
                    if (error) {
                        displayError.textContent = error.message;
                    } else {
                        displayError.textContent = '';
                    }
                });
            }

            statusDiv.innerHTML = '<div class="success">Stripe initialized successfully</div>';
            document.getElementById('create-token-button').disabled = false;
            isInitialized = true;

        } catch (error) {
            statusDiv.innerHTML = '<div class="error">Error initializing Stripe: ' + error.message + '</div>';
            console.error('Stripe initialization error:', error);
            isInitialized = false;
        }
    }

    function handleTokenTypeChange() {
        // Auto re-initialize when token type changes if Stripe was already initialized
        if (isInitialized) {
            initializeStripe();
        }
    }

    async function createToken() {
        const tokenType = document.getElementById('token-type').value;
        const cardholderName = document.getElementById('cardholder-name').value;
        const email = document.getElementById('email').value;
        const resultDiv = document.getElementById('token-result');
        const createButton = document.getElementById('create-token-button');

        if (!cardholderName || !email) {
            resultDiv.innerHTML = '<div class="error">Please fill in all customer information</div>';
            return;
        }

        // Disable button during processing
        createButton.disabled = true;
        createButton.textContent = 'Creating Token...';
        resultDiv.innerHTML = '';

        try {
            let token, error;

            if (tokenType === 'confirmation') {
                // Submit the elements first (required for confirmation tokens)
                const submitResult = await elements.submit();
                if (submitResult.error) {
                    error = submitResult.error;
                } else {
                    // Create Confirmation Token
                    const result = await stripe.createConfirmationToken({
                        elements,
                        params: {
                            payment_method_data: {
                                billing_details: {
                                    name: cardholderName,
                                    email: email,
                                    phone: '', // Required since we're using setup_future_usage
                                    address: {
                                        line1: '',
                                        line2: '',
                                        city: '',
                                        state: '',
                                        postal_code: '',
                                        country: 'US'
                                    }
                                }
                            }
                        }
                    });
                    token = result.confirmationToken;
                    error = result.error;
                }

            } else if (tokenType === 'payment-method') {
                // Check if paymentMethodElements is properly initialized
                if (!paymentMethodElements) {
                    error = { message: 'Payment Method Elements not initialized. Please click "Initialize Stripe" first.' };
                } else {
                    // Submit the elements first (required for Payment Element)
                    const submitResult = await paymentMethodElements.submit();
                    if (submitResult.error) {
                        error = submitResult.error;
                    } else {
                        // Create Payment Method Token
                        const result = await stripe.createPaymentMethod({
                            elements: paymentMethodElements,
                            params: {
                                billing_details: {
                                    name: cardholderName,
                                    email: email,
                                }
                            }
                        });
                        token = result.paymentMethod;
                        error = result.error;
                    }
                }

            } else {
                // Create legacy token using Card Element
                const result = await stripe.createToken(cardElement, {
                    name: cardholderName,
                    address_line1: '', // Can be empty for test
                    address_city: '',
                    address_state: '',
                    address_zip: '',
                    address_country: 'US',
                });
                token = result.token;
                error = result.error;
            }

            // Re-enable button
            createButton.disabled = false;
            createButton.textContent = 'Create Token';

            if (error) {
                console.error('Token creation failed:', error);
                resultDiv.innerHTML = `<div class="error">Error: ${error.message}</div>`;
            } else {
                console.log('Token created:', token);

                const tokenTypeLabels = {
                    'confirmation': 'Confirmation Token',
                    'payment-method': 'Payment Method Token',
                    'legacy': 'Legacy Token'
                };

                resultDiv.innerHTML = `
                        <div class="success">
                            <strong>Success!</strong><br>
                            <strong>Type:</strong> ${tokenTypeLabels[tokenType]}<br>
                            <strong>Token ID:</strong> <code>${token.id}</code><br>
                            <small>Use this ID as payment_token in your Vrio API call with payment_method_id = 15</small>
                        </div>
                    `;
            }

        } catch (error) {
            console.error('Exception creating token:', error);
            createButton.disabled = false;
            createButton.textContent = 'Create Token';
            resultDiv.innerHTML = `<div class="error">Error: ${error.message}</div>`;
        }
    }
</script>
</body>
</html>

Sending Tokens to Vrio API

Once you have obtained a payment token from Stripe.js, you can process the payment through Vrio's API.

Step 1: Configure Your Merchant Account

Ensure your Stripe merchant account is properly configured in your Vrio dashboard with the appropriate API keys and has Payment Token as an available payment method.

Step 2: Create an Order

First, create an order using Vrio's order creation endpoint ( NOTE: you can also pass the payment_token in Step 1 if you are using the process=action when creating the order ):

curl -X POST "https://api.vrio.app/orders" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -d '{
    "connection_id": 1,
    "campaign_id": 18,
    "offers": "[{\"offer_id\":89, \"order_offer_quantity\": 1,\"order_offer_price\":0.50}]",
    "email": "[email protected]",
    "bill_fname": "John",
    "bill_lname": "Doe",
    "bill_phone": "+1234567890",
    "bill_address1": "123 Main St",
    "bill_city": "Anytown",
    "bill_state": "NY",
    "bill_zipcode": "12345",
    "bill_country": "US",
    "ship_address1": "123 Main St",
    "ship_city": "Anytown",
    "ship_state": "NY", 
    "ship_zipcode": "12345",
    "ship_country": "US"
  }'

This will return an order object with an order_id.

Step 3: Process Payment with Token

Use the order processing endpoint with your Stripe payment token:

Confirmation Token (Recommended)

curl -X POST "https://api.vrio.app/orders/{order_id}/process" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -d '{
    "payment_method_id": 15, // pass 3 for Google Pay and 4 for Apple Pay
    "payment_token": "ctoken_1Nxxxxxxxxxxxxxx",
    "merchant_id": 456
  }'

Payment Method Token

curl -X POST "https://api.vrio.app/orders/{order_id}/process" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -d '{
    "payment_method_id": 15, // pass 3 for Google Pay and 4 for Apple Pay
    "payment_token": "pm_1Nxxxxxxxxxxxxxx",
    "merchant_id": 456
  }'

Legacy Token (Alternative)

curl -X POST "https://api.vrio.app/orders/{order_id}/process" \
  -H "Content-Type: application/json" \
  -H "X-API-KEY: YOUR_API_KEY" \
  -d '{
    "payment_method_id": 15,
    "payment_token": "tok_1Nxxxxxxxxxxxxxx",
    "merchant_id": 456
  }'

Common Issues

"Invalid token id" Error

  • Ensure you're using the correct token format (ctoken_, pm_, or tok_)
  • Ensure the token being passed belongs to the correct merchant in Vrio
  • Verify your Stripe keys match the token's mode (test vs live)
  • Check that the token was created successfully before sending to Vrio

Token Creation Fails

  • Verify your Stripe publishable key is correct
  • Ensure card information is valid (for Payment Method and Legacy tokens)
  • For confirmation tokens, ensure Payment Element is properly configured
  • Check browser console for Stripe.js errors

For additional support, consult the Stripe Testing Guide and Stripe.js Reference.