Client-Side Implementation
Checkout form
Dual-branded cards
Dual-branded cards include both the UnionPay logo and another card brand logo – such as Visa, Mastercard, or American Express – and can be processed through either brand's network. If your business is based outside of China, card network rules around dual-branded UnionPay cards dictate that your customers must be able to select which card brand they want their transaction to process through at checkout.
To maintain compliance with this rule, make sure to include an option in your checkout form for customers to select UnionPay as their card brand. You should only process UnionPay cards directly via the UnionPay network if the customer specifies that they want the card processed this way. Otherwise, dual-branded cards should be processed via the other card brand network.
Card capabilities
UnionPay cards can either be credit or debit. This capability determines what information you will need to collect in your checkout form, as well as your options for processing the transaction on your server:
- Credit cards
- Require CVV and expiration date
- Support both immediate and delayed settlement – you can either submit the transaction for settlement upon creation or make a separate submit for settlement call
- Debit cards
- Do not always require CVV or expiration date
- Support only immediate settlement
Setup
script
tags to load files, be sure to at least include:
- HTML
<script src="https://js.braintreegateway.com/web/3.111.0/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.111.0/js/unionpay.min.js"></script>
Check card capabilities
UnionPay only supports authorization via client tokens.
Use the card number to check the card capabilities. This should be done as soon as the customer has finished entering the card number, since you may need to update your form based on the response:
- Callbacks
- Promises
var numberField = document.querySelector('#card-number-field');
braintree.client.create({
authorization: 'CLIENT_TOKEN_FROM_SERVER'
}, function (clientErr, clientInstance) {
if (clientErr) {
console.error('Error creating client:', clientErr);
return;
}
// Create a UnionPay component
braintree.unionpay.create({
client: clientInstance
}, function (unionpayErr, unionPayInstance) {
if (unionpayErr) {
console.error('Error creating unionPay:', unionpayErr);
return;
}
// Fetch card capabilities when customer has finished entering card number
numberField.addEventListener('blur', function (event) {
fetchCapabilities(unionPayInstance);
}, false);
});
});
function fetchCapabilities(unionPay) { unionPay.fetchCapabilities({ card: { number:
numberField.value } }, function (err, cardCapabilities) { if (err) { console.error('Error fetching
card capabilities:', err); return; }
// Determines if the card is UnionPay
if (cardCapabilities.isUnionPay) {
if (cardCapabilities.unionPay && !cardCapabilities.unionPay.isSupported) {
// This UnionPay card can't be processed by Braintree.
// Ask the customer to try a different card
}
if (cardCapabilities.isDebit) {
// CVV and expiration date fields not required
} else {
// CVV and expiration date fields required
}
// Show mobile phone number field
}
});
}
Enrollment and tokenization
UnionPay cards require enrollment before they can be tokenized. The enrollment process may require customer action, where UnionPay sends an authorization code via SMS to the customer's mobile phone number. Your integration then collects the SMS auth code from the customer and provides it to us to tokenize the card.
If the enrollment indicates that the SMS code is not required, you can tokenize it immediately.
When an enrollment is completed for a UnionPay card, you will no longer be able to update the card number, expiration date, expiration month, or expiration year.
Begin by collecting the card details and customer's mobile phone number:
- Callbacks
- Promises
unionPay.enroll({
card: {
number: '6211111111111111',
expirationMonth: '12',
expirationYear: '2038'
},
mobile: {
countryCode: '62',
number: '11111111111'
}
}, function (err, response) {
if (response.smsCodeRequired) {
// Customer will be sent an SMS code.
// Show UI for collecting that SMS code.
} else {
// SMS code not sent or required.
// Tokenization can happen immediately.
unionPay.tokenize(...);
}
// ...
});
When attempting to enroll a UnionPay card, it is your responsibility to provide it with the enrollment ID and (if required) the SMS auth code collected from the customer during tokenization:
- Callbacks
- Promises
unionPay.enroll({
card: {
number: '6211111111111111',
expirationMonth: '12',
expirationYear: '2038'
},
mobile: {
countryCode: '62',
number: '11111111111'
}
}, function(err, response) {
if (err) {
console.error('Error during enroll:', err);
return;
}
if (response.smsCodeRequired) {
// Customer will be sent an SMS code.
// Get SMS auth code from customer input
waitForAuthCodeFromCustomer(function (smsAuthCode) {
// Include the SMS auth code and enrollment ID when tokenizing
unionPay.tokenize({
card: {
number: '6211111111111111',
expirationMonth: '12',
expirationYear: '2038',
cvv: '123'
},
smsCode: smsAuthCode,
enrollmentId: response.enrollmentId
}, function (err, nonce) {
// Handle tokenization result.
});
});
} else {
// SMS code not sent or required.
// Include the enrollment ID when tokenizing
unionPay.tokenize({
card: {
number: '6211111111111111',
expirationMonth: '12',
expirationYear: '2038',
cvv: '123'
},
enrollmentId: response.enrollmentId
}, function (err, nonce) {
// Handle tokenization result.
});
});
}});
Validating UnionPay is performed during the
enrollment
process. As such, there is no extra validation process that can be performed
during tokenization and the validate
property should not be passed when
tokenizing a UnionPay card.
UnionPay with Hosted Fields
For a SAQ A PCI compliant UnionPay integration, you can use our Hosted Fields form to collect card information. By using UnionPay with Hosted Fields, you can use the same form for both UnionPay and traditional credit cards.
This guide includes a basic Hosted Fields integration. For more information about configuration and styling, see our Hosted Fields guide.
Create your components
To set up a UnionPay with Hosted Fields integration, you create two separate Hosted Fields and UnionPay components.
- HTML
<form action="/" id="my-form" method="post">
<label for="card-number">Card Number</label>
<div id="card-number"></div>
<label for="expiration-date">Expiration Date</label>
<div id="expiration-date"></div>
<label for="cvv">CVV</label>
<div id="cvv"></div>
<label for="postal-code">Postal Code</label>
<div id="postal-code"></div>
<span id="unionpay" hidden>
<label id="country-code-label" for="country-code">Country Code</label>
<input id="country-code" type="text" name="country-code" placeholder="62" />
<label id="mobile-number-label" for="mobile-number">Mobile Phone Number</label>
<input id="mobile-number" type="text" name="mobile-number" placeholder="1111111111" />
<label id="sms-code-label" for="sms-code">SMS Auth Code</label>
<input id="sms-code" type="text" name="sms-code" placeholder="11111" />
<button type="button" id="enroll-button">Verify</button>
</span>
<input disabled type="submit" value="Submit Transaction" />
</form>
- Callbacks
- Promises
braintree.client.create({authorization: 'CLIENT_TOKEN_FROM_SERVER'}, function (err, clientInstance) {
braintree.hostedFields.create({
client: clientInstance,
fields: {
number: {container: '#card-number'},
expirationDate: {container: '#expiration-date'},
cvv: {container: '#cvv'},
postalCode: {container: '#postal-code'}
}
}, function (hostedFieldsErr, hostedFieldsInstance) {
braintree.unionpay.create({client: clientInstance}, function (unionPayErr, unionpayInstance) {
// This is where you will collect UnionPay data
});
});
});
Getting card capabilities
You must fetch the capabilities of a card to determine which information is necessary to process it.
In your flow, call fetchCapabilities
once the customer has provided a valid card number in the
number
field. You can use the
{{jsReferenceUrl path='/HostedFields.html#on' text='events emitted by Hosted Fields'}}
to
determine when a user has entered a card number.
- Callbacks
- Promises
// This occurs inside of the Hosted Fields \`create\` callback
braintree.unionpay.create({client: clientInstance}, function (unionPayErr, unionpayInstance) {
hostedFieldsInstance.on('validityChange', function (event) { if (!(event.emittedBy === 'number' &&
event.fields.number.isValid)) { return; }
unionpayInstance.fetchCapabilities({ hostedFields: hostedFieldsInstance }, function (fetchErr, cardCapabilities) {
if (fetchErr) { throw fetchErr; }
if (cardCapabilities.isUnionPay) {
if (cardCapabilities.isDebit) {
// CVV and expiration date fields are not required for all UnionPay debit cards.
// Indicate that these fields are optional on your form.
}
// Show your own mobile phone number inputs for enrollment
if (cardCapabilities.unionPay && !cardCapabilities.unionPay.isSupported) {
// This UnionPay card can't be processed by Braintree.
// Ask the customer to try a different card.
}
}
});
}); });
Enrollment
You can enroll using your Hosted Fields instance and the mobile data you've collected.
- Callbacks
- Promises
// This occurs inside of the Hosted Fields \`create\` callback
braintree.unionpay.create({client: clientInstance}, function (unionpayErr, unionpayInstance) {
var enrollButton = document.querySelector('#enroll-button'); // Button to enroll in UnionPay
var unionpayEnrollmentId;
hostedFieldsInstance.on('validityChange', function (event) { if (event.emittedBy === 'number' &&
event.fields.number.isValid) { // Card is valid; fetch capabilities... } });
enrollButton.addEventListener('click', function (event) { event.preventDefault();
unionpayInstance.enroll({
hostedFields: hostedFieldsInstance,
// Collect these values using your own inputs
mobile: {
countryCode: '62',
number: '11111111111'
}
}, function (enrollErr, enrollData) {
if (enrollErr) { throw enrollErr; }
unionpayEnrollmentId = enrollData.enrollmentId;
if (enrollData.smsCodeRequired) {
// Show input to collect SMS auth code sent to customer during enrollment
} else {
// SMS auth code not required, proceed to tokenize
}
});
}, false); });
Tokenization
When using UnionPay with Hosted Fields, your method of tokenization depends on the requirements of the card.
If the card is a UnionPay card that requires enrollment, you will tokenize using your UnionPay
instance, the enrollmentId
you received from your enroll
call, and (if required) the smsCode
received by the customer on their mobile phone after enrollment.
- Callbacks
- Promises
unionpayInstance.tokenize({
hostedFields: hostedFieldsInstance,
enrollmentId: unionpayEnrollmentId,
smsCode: '11111' // Only required if smsCodeRequired. Can be left blank or undefined otherwise.
}, function (err, unionpayTokenizeData) {
if (err) { throw err; }
// Submit unionpayTokenizeData.nonce to your server });
If the card is not a UnionPay card you will tokenize using your Hosted Fields instance to retrieve your nonce.
- Callbacks
- Promises
hostedFieldsInstance.tokenize(function (err, hostedFieldsTokenizeData) {
if (err) { throw err; }
// Submit hostedFieldsTokenizeData.nonce to your server });
Below is a full example of a UnionPay with Hosted Fields tokenization flow:
- Callbacks
- Promises
// This occurs inside of the Hosted Fields \`create\` callback
braintree.unionpay.create({client: clientInstance}, function (unionpayErr, unionpayInstance) {
var enrollButton = document.querySelector('#enroll-button'); // Button to enroll in UnionPay
var submitButton = document.querySelector('input[type="submit"]'); // Button to submit your form
var isUnionPay; // Obtained during fetch capabilities
var unionpayEnrollmentId; // Obtained during enroll
hostedFieldsInstance.on('validityChange', function (event) { if (event.emittedBy === 'number' &&
event.fields.number.isValid) { // Card is valid; fetch capabilities... } });
enrollButton.addEventListener('click', function (event) { // Enrollment if applicable... }, false);
submitButton.addEventListener('click', function (event) { event.preventDefault();
if (isUnionPay) {
// Tokenize with UnionPay, using Hosted Fields instance
unionpayInstance.tokenize({
hostedFields: hostedFieldsInstance,
enrollmentId: unionpayEnrollmentId,
smsCode: '11111' // Only required if smsCodeRequired. Can be left blank or undefined otherwise.
}, function (unionpayTokenizeErr, unionpayTokenizeData) {
if (unionpayTokenizeErr) { throw unionpayTokenizeErr; }
// Submit unionpayTokenizeData.nonce to your server
});
} else {
// Tokenize with Hosted Fields
hostedFieldsInstance.tokenize(function (hostedFieldsTokenizeErr, hostedFieldsTokenizeData) {
if (hostedFieldsTokenizeErr) { throw hostedFieldsTokenizeErr; }
// Submit hostedFieldsTokenizeData.nonce to your server
});
}
}, false); });
You can find out more about UnionPay in the
{{{jsReferenceUrl path='/UnionPay.html' text='JavaScript SDK v3 reference'}}}
.
Special considerations
AVS and CVV validation does not apply
AVS and CVV validation does not apply to UnionPay cards, since the enrollment process handles card validation. Any AVS and CVV rules you've enabled for credit cards will be ignored for UnionPay cards.