Saving a token to capture future payments
You can handle many types of purchase using simple one-off payments. However, you might want to let customers store their card details so they can make future payments more quickly. Also, recurring payments (such as subscriptions and instalments) are easier to handle using stored card details because you can take payment from them automatically without any action from the customer.
The Smart Router API lets you create Customer
objects to store various details about customers, including
payment details from one or more cards. Once you have created a
Customer
, you can generate a customer token from it which you
can then use in place of a card token to handle payments. However,
unlike a card token, a customer token can be reused repeatedly.
Below is a sequence diagram that shows the flow for creating a customer token. The following sections explain the steps in more detail.
Step 1: Create a customer
The first step is to create a new Customer
object
on the server side.
The object has numerous fields to store data about your customer, including
contact details and card details. Although you can create a new Customer
without supplying any data, we strongly recommend that you add all
the data you have available to the object. This data can help you locate a Customer
when you search in the Dashboard
and Smart Router can also use it to improve routing performance and
decrease the risk of fraudulent payments. See the Customer
section of the API reference for details of all the object’s data fields.
The code sample below shows how to create a new Customer
object. Note that
once the object is created, you should store its id
property in your database
or otherwise keep track of it. You will need the id
value to retrieve the
object in the future. Note that the currency
field used in the example
is optional but it will be set to the currency of the first payment the
customer makes if you choose not to set it during creation. Once currency
has been set for a Customer
, its value cannot be changed.
See the page about setting up the APIs to learn how
to install the required library for your language and create the client
object to access the API.
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 customer = client.newCustomer().create({
first_name: "John",
last_name: "Smith",
currency: "USD"
}).then(function(customer) {
//
}, function(err) {
// An error occured
});
customer = client.new_customer().create({
"first_name": "John",
"last_name": "Smith",
"currency": "USD"
})
customer = client.customer.create(
first_name: "John",
last_name: "Smith",
currency: "USD"
)
$customer = $client->newCustomer()->create(array(
"first_name" => "John",
"last_name" => "Smith",
"currency" => "USD"
));
cust, err := client.NewCustomer().Create(processout.CustomerCreateParameters{
Customer: &processout.Customer{
FirstName: processout.String("John"),
LastName: processout.String("Smith"),
Currency: processout.String("USD"),
},
})
Step 2: Create a customer token
You can use a Customer
object and a
card token together
to create a customer token. You can use this in place of a
card token to capture payments.
There are 2 different approaches you can use to create the customer token from the card token:
- Without verification: With this approach, you supply an existing card token as a parameter when you create the customer token on the server. The API does not check the validity of the card details before creating the token.
- With verification: Here, you do not supply a card token on the server side but instead use an extra verification step with a card token on the client side. This checks that the card details are valid before creating the customer token.
The following code samples show the difference between these approaches:
curl -X POST https://api.processout.com/customers/cust_WtaVdUjAGpOlbLiYWYXBR67whr91Rlks/tokens \
-u test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x:key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB \
-d source=card_Tpu6ZOCDu1tnDKp0kTnPOcVDMUbW7dTU
client.newToken().create({
customer_id: "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
source: "card_Tpu6ZOCDu1tnDKp0kTnPOcVDMUbW7dTU"
}).then(function(token) {
//
}, function(err) {
// An error occured
});
token = client.new_token().create({
"customer_id": "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
"source": "card_Tpu6ZOCDu1tnDKp0kTnPOcVDMUbW7dTU"
})
token = client.token.create(
customer_id: "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
source: "card_Tpu6ZOCDu1tnDKp0kTnPOcVDMUbW7dTU"
)
$token = $client->newToken()->create(array(
"customer_id" => "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
"source" => "card_Tpu6ZOCDu1tnDKp0kTnPOcVDMUbW7dTU"
));
token, err := client.NewToken().Create(&processout.TokenCreateParameters{
Token: &processout.Token{
CustomerID: processout.String("cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj"),
},
Source: "card_Tpu6ZOCDu1tnDKp0kTnPOcVDMUbW7dTU",
})
curl -X POST https://api.processout.com/customers/cust_WtaVdUjAGpOlbLiYWYXBR67whr91Rlks/tokens \
-u test-proj_gAO1Uu0ysZJvDuUpOGPkUBeE3pGalk3x:key_sandbox_mah31RDFqcDxmaS7MvhDbJfDJvjtsFTB \
-d verify=true
client.newToken().create({
customer_id: "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
verify: true
}).then(function(token) {
//
}, function(err) {
// An error occured
});
token = client.new_token().create({
"customer_id": "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
"verify": True
})
token = client.token.create(
customer_id: "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
verify: true
)
$token = $client->newToken()->create(array(
"customer_id" => "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
"verify" => true
));
token, err := client.NewToken().Create(&processout.TokenCreateParameters{
Token: &processout.Token{
CustomerID: processout.String("cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj"),
},
Verify: true,
})
If you create the token object without verification then you can immediately use
the string in its id
field to capture payment
in the same way you would with a card token string. If you do request verification
then you must follow an extra step on the client side, as described below.
Step 3: Verify the token on the client side
The customer token object has a verification_status
field
whose possible values are success
, pending
, failed
, not-requested
and
unknown
. When you create a token without verification, this will be set
to pending
which means that you cannot use it for payment yet.
You must pass the id
values from both the Customer
object and the
customer Token
object to the client to perform the verification, as shown
in the code sample below. You will
also need a payment source token on the client side, such as a card token.
For example, you could display a form
to let the customer enter the details of a new card to associate with the
token.
Note that in some cases, the Payment Service Provider (PSP) will not respond synchronously to the verification request. When this happens, you can still get confirmation that the card was verified using webhooks. See the API reference section about Customer token events for more information.
For mobile, 3DS2 flow is native. See the page about mobile app payments to learn more about the
handler
function shown in the code sample.
// Make sure the card ID, customer ID and token ID are available
// in the same scope as the makeCardToken() call
client.makeCardToken(
"card_1jSEVrx7oaRta1KEdxoMWbiGkK2MijrZ",
"cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
"tok_fKK4btSG7wd13ZZaevzhMcuNbpjcu1Zy",
{
// Use this if you want to offer the customer a preferred scheme
// to pay on (for example, if the customer's card supports
// co-schemes such as carte bancaire)
preferred_scheme: "carte bancaire"
}, function(customerTokenID) {
var field = document.createElement("input");
field.type = "hidden";
field.name = "customer_token_id";
field.value = customerTokenID;
// Re-enable the button
document.getElementById("paymentBtn").disabled = false;
// Add the customer_token_id input so that it gets sent back to the server.
// Finally, 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 the same scope as the makeCardToken() call
var tokenRequest = TokenRequest(
source: "card_1jSEVrx7oaRta1KEdxoMWbiGkK2MijrZ",
customerID: "cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj",
tokenID: "tok_fKK4btSG7wd13ZZaevzhMcuNbpjcu1Zy"
);
// optional
tokenRequest.thirdPartySDKVersion = ThreeDS2Service.ADY3DS2SDKVersion()
tokenRequest.preferredScheme = "carte bancaire"
ProcessOut.makeCardToken(
tokenRequest,
handler,
with: self
);
// Make sure the card ID, customer ID and token ID are available
// in the same scope as the makeCardToken() call
TokenRequest tokenRequest = new TokenRequest(
"tok_fKK4btSG7wd13ZZaevzhMcuNbpjcu1Zy", // tokenID
"cust_C4hZXQTU0aWoYeenHYC0DektYDqf8ocj", // customerID
"card_1jSEVrx7oaRta1KEdxoMWbiGkK2MijrZ" // source
);
// optional
tokenRequest.setThirdPartySDKVersion(ThreeDS2Service.getSDKVersion())
tokenRequest.setPreferredScheme("carte bancaire")
client.makeCardToken(
tokenRequest,
handler
);
When the PSP has verified the customer token successfully, you can start using it to capture payments. Note that for Cardholder Initiated Transactions (CITs), you should authorize payment on the client before capturing to comply with Strong Customer Authentication laws.
Listing and deleting customers’ tokens
You can create a maximum of 150 tokens for a Customer
object, so
you can let the customer set up a selection of cards or other payment methods to
choose from.
The code sample below shows how to obtain a list of all tokens for a particular
customer and also how to delete an unwanted token from the list (which
you might need to do when a card expires, say).
curl -X GET https://api.processout.com/customers/cust_WtaVdUjAGpOlbLiYWYXBR67whr91Rlks/tokens \
-u 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
});
tokens = customer.fetch_tokens()
# And let's say our customer wants to remove its first token
tokens[0].delete()
tokens = customer.fetch_tokens()
# And let's say our customer wants to remove its first token
tokens.first.delete
$tokens = $customer->fetchTokens();
// And let's say our customer wants to remove its first token
$tokens[0]->delete();
tok, _ := cust.FetchTokens()
// And let's say our customer wants to remove its first token
tok.Get().Delete()