Save a token to capture future payments

While most e-commerce businesses handle one-off payments, you might need to ask for a customer’s card and capture payments on it in the future (one, but also several if needed). This is done by tokenizing the card token created by ProcessOut.js.


Step 1: Create a customer

Because tokens are specifically linked to one of your customers, you must first create a Customer resource.

You can create any number of customer you may need. A customer object may be entirely empty, but we strongly advise you to fill the object with as much data as you have. This can help doing searches on your dashboard, but it also greatly helps us when smart routing transactions related to this customer and decreases fraud risks. You can find all the available Customer attributes here ↗.

The ID of the newly created customer should also be stored in your application or database so you can use it later.

curl -X POST https://api.processout.com/customers \
    -u test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x:key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB \
    -d first_name=John \
    -d last_name=Smith \
    -d currency=USD
var ProcessOut = require("processout");
var client = new ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x",
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB");

var customer = client.newCustomer().create({
    first_name: "John",
    last_name:  "Smith",
    currency:   "USD"
}).then(function(customer) {
    //

}, function(err) {
    // An error occured

});
import processout
client = processout.ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB")

customer = client.new_customer().create({
    "first_name": "John",
    "last_name":  "Smith",
    "currency":   "USD"
})
require "processout"
client = ProcessOut::Client.new(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB")

customer = client.customer.create(
    first_name: "John",
    last_name:  "Smith",
    currency:   "USD"
)
<?php
$client = new \ProcessOut\ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB");

$customer = $client->newCustomer()->create(array(
    "first_name" => "John",
    "last_name"  => "Smith",
    "currency"   => "USD"
));
import "github.com/processout/processout-go"

var client = processout.New(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB",
)

cust, err := client.NewCustomer().Create(processout.CustomerCreateParameters{
    Customer: &processout.Customer{
        FirstName: processout.String("John"),
        LastName:  processout.String("Smith"),
        Currency:  processout.String("USD"),
    },
})

Note: While the currency field is also optionnal, it will be automatically set to the currency of the first invoice your customer will pay. It is also immutable.

Step 2: Create a customer token

Now that we have a customer object to work with, we can create tokens that’ll belong to it. Customer tokens can be created from the ones returned by ProcessOut.js or by our mobile SDKs ↗ and can be used any number of time.

You have two ways of creating a Customer Token:

  • without verifying its source; or
  • by verifying its source while creating the Customer Token, which will make the creation fail if the source is invalid (such as an invalid card).
Without verification
curl -X POST https://api.processout.com/customers/cust_WtaVdUjAGpOlbLiYWYXBR67whr91Rlks/tokens \
    -u test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x:key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB \
    -d source=card_Tpu6ZOCDu1tnDKp0kTnPOcVDMUbW7dTU
var ProcessOut = require("processout");
var client = new ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x",
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB");

client.newToken().create({
    customer_id: "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
    source:      "card_Tpu6ZOCDu1tnDKp0kTnPOcVDMUbW7dTU"
}).then(function(token) {
    // 

}, function(err) {
    // An error occured

});
import processout
client = processout.ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB")

token = client.new_token().create({
    "customer_id": "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
    "source":      "card_Tpu6ZOCDu1tnDKp0kTnPOcVDMUbW7dTU"
})
require "processout"
client = ProcessOut::Client.new(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB")

token = client.token.create(
    customer_id: "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
    source:      "card_Tpu6ZOCDu1tnDKp0kTnPOcVDMUbW7dTU"
)
<?php
$client = new \ProcessOut\ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB");

$token = $client->newToken()->create(array(
    "customer_id" => "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
    "source"      => "card_Tpu6ZOCDu1tnDKp0kTnPOcVDMUbW7dTU"
));
import "github.com/processout/processout-go"

var client = processout.New(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB",
)

token, err := client.NewToken().Create(&processout.TokenCreateParameters{
    Token: &processout.Token{
        CustomerID: processout.String("cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj"),
    },
    Source: "card_Tpu6ZOCDu1tnDKp0kTnPOcVDMUbW7dTU",
})

With verification
curl -X POST https://api.processout.com/customers/cust_WtaVdUjAGpOlbLiYWYXBR67whr91Rlks/tokens \
    -u test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x:key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB \
    -d verify=true
var ProcessOut = require("processout");
var client = new ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x",
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB");

client.newToken().create({
    customer_id: "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
    verify:      true
}).then(function(token) {
    // 

}, function(err) {
    // An error occured

});
import processout
client = processout.ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB")

token = client.new_token().create({
    "customer_id": "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
    "verify":      True
})
require "processout"
client = ProcessOut::Client.new(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB")

token = client.token.create(
    customer_id: "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
    verify:      true
)
<?php
$client = new \ProcessOut\ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB");

$token = $client->newToken()->create(array(
    "customer_id" => "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
    "verify"      => true
));
import "github.com/processout/processout-go"

var client = processout.New(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB",
)

token, err := client.NewToken().Create(&processout.TokenCreateParameters{
    Token: &processout.Token{
        CustomerID: processout.String("cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj"),
    },
    Verify: true,
})

Note the difference: when not doing any verification, you will send the source in the Customer Token creation call directly, directly from your backend. Once you have created the Customer Token without verification, it may be used to attempt payments directly: jump to Step 4: Capture a payment using the customer token ↗.

If you rather want to operate a verification of the token, you must not send the source yourself but rather set the verify flag.

Step 2.b: Verify Customer Token from front-end

Now that a Customer Token is created, it still is in a pending state and awaits an actual payment source to be assigned to it. Such payment source can for example be a Card. To link one, send the Customer Token ID to your front-end.

For mobile, 3DS2 flow is native. For full documentation on how to handle the `handler` function parameter, please refer to the more in-depth mobile SDKs reference ↗.
// Make sure the card ID, customer ID and token ID are available
// in your makeCardToken scope
client.makeCardToken(
    "card_1jSEVrx7oaRta1KEdxoMWbiGkK2MijrZ", 
    "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj", 
    "tok_fKK4btSG7wd13ZZaevzhMcuNbpjcu1Zy", 
    {}, function(customerTokenID) {
        var field   = document.createElement("input");
        field.type  = "hidden";
        field.name  = "customer_token_id";
        field.value = customerTokenID;

        // Enable back the button
        document.getElementById("paymentBtn").disabled = false;

        // We add the customer_token_id input so that it’s sent back to the server.
        // The only thing left to do is to submit the form
        formElement.appendChild(field);
        formElement.submit();
    }, function(err) {
        document.getElementById("errors").innerHTML = err.message;
    });
// Make sure the card ID, customer ID and token ID are available
// in your makeCardToken scope
ProcessOut.makeCardToken(
    "card_1jSEVrx7oaRta1KEdxoMWbiGkK2MijrZ", 
    "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
    "tok_fKK4btSG7wd13ZZaevzhMcuNbpjcu1Zy",
    handler,
    with: self
);
// Make sure the card ID, customer ID and token ID are available
// in your makeCardToken scope
client.makeCardToken(
    "card_1jSEVrx7oaRta1KEdxoMWbiGkK2MijrZ", 
    "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
    "tok_fKK4btSG7wd13ZZaevzhMcuNbpjcu1Zy",
    handler
);

Step 3: Capture a payment using the customer token

After having created a customer token, it is possible to use it to capture any invoice of any amount at any given time. Simply pass along the Customer Token ID as the source when authorizing or capturing a transaction.

If you’re using the Customer Tokens to initiate one-click purchases of orders directly requested by the Customer (meaning, not automatically generated from your back-end for subscriptions or top-ups), these purchases might require SCA. You can make use of ProcessOut.js to properly handle this case when necessary ↗.
curl -X POST https://api.processout.com/invoices/iv_lEZFFcQg5WwpKcolBrlzioeeFnnuPmyE/capture \
    -u test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x:key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB \
    -d source=tok_fKK4btSG7wd13ZZaevzhMcuNbpjcu1Zy
var ProcessOut = require("processout");
var client = new ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x",
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB");

invoice.capture("tok_fKK4btSG7wd13ZZaevzhMcuNbpjcu1Zy").then(
    function(transaction) {
        // 

    }, function(err) {
        // The invoice could not be captured
    });
import processout
client = processout.ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB")

transaction = invoice.capture("tok_fKK4btSG7wd13ZZaevzhMcuNbpjcu1Zy")
require "processout"
client = ProcessOut::Client.new(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB")

transaction = invoice.capture("tok_fKK4btSG7wd13ZZaevzhMcuNbpjcu1Zy")
<?php
$client = new \ProcessOut\ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB");

$transaction = $invoice->capture("tok_fKK4btSG7wd13ZZaevzhMcuNbpjcu1Zy");
import "github.com/processout/processout-go"

var client = processout.New(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB",
)

tr, _ := iv.Capture("tok_fKK4btSG7wd13ZZaevzhMcuNbpjcu1Zy")

Listing and deleting customers’ tokens

Once you’ve stored a token for a customer, you might want to list them so you can show them to your customers, or delete them when your customers want to remove the payment method.

curl -X GET https://api.processout.com/customers/cust_WtaVdUjAGpOlbLiYWYXBR67whr91Rlks/tokens \
    -u test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x:key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB
var ProcessOut = require("processout");
var client = new ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x",
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB");

customer.fetchTokens().then(
    function(tokens) {
        // And let's say our customer wants to remove its first token
        tokens[0].delete(); 

    }, function(err) {
        // The customer's tokens could not be fetched
    });
import processout
client = processout.ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB")

tokens = customer.fetch_tokens()

# And let's say our customer wants to remove its first token
tokens[0].delete()
require "processout"
client = ProcessOut::Client.new(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB")

tokens = customer.fetch_tokens()

# And let's say our customer wants to remove its first token
tokens.first.delete
<?php
$client = new \ProcessOut\ProcessOut(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB");

$tokens = $customer->fetchTokens();

// And let's say our customer wants to remove its first token
$tokens[0]->delete();
import "github.com/processout/processout-go"

var client = processout.New(
    "test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x", 
    "key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB",
)

tok, _ := cust.FetchTokens()

// And let's say our customer wants to remove its first token
tok.Get().Delete()