'use strict';
var BraintreeError = require('../../lib/error');
var analytics = require('../../lib/analytics');
/**
* @class
* @param {object} options see {@link module:braintree-web/unionpay.create|unionpay.create}
* @description <strong>Do not use this constructor directly. Use {@link module:braintree-web/unionpay.create|braintree-web.unionpay.create} instead.</strong>
* @classdesc This class represents a UnionPay component. Instances of this class have methods for {@link UnionPay#fetchCapabilities fetching capabilities} of UnionPay cards, {@link UnionPay#enroll enrolling} a UnionPay card, and {@link UnionPay#tokenize tokenizing} a UnionPay card.
*/
function UnionPay(options) {
this._options = options;
this._merchantAccountId = options.client.getConfiguration().gatewayConfiguration.unionPay.merchantAccountId;
}
/**
* @typedef {object} UnionPay~fetchCapabilitiesPayload
* @property {boolean} isUnionPay Determines if this card is a UnionPay card
* @property {boolean} isDebit Determines if this card is a debit card
* @property {object} unionPay UnionPay specific properties
* @property {boolean} unionPay.supportsTwoStepAuthAndCapture Determines if the card allows for an authorization, but settling the transaction later
* @property {boolean} unionPay.isUnionPayEnrollmentRequired Notifies if {@link UnionPay#enroll|enrollment} should be completed
*/
/**
* Fetches the capabilities of a card, including whether or not the card needs to be enrolled before use. If the card needs to be enrolled, use {@link UnionPay#enroll|enroll}
* @public
* @param {object} options UnionPay {@link UnionPay#fetchCapabilities fetchCapabilities} options
* @param {object} options.cardNumber The card number to fetch capabilities for
* @param {errback} errback The second argument, <code>data</code>, is a {@link UnionPay#fetchCapabilitiesPayload fetchCapabilitiesPayload}
* @returns {void}
*/
UnionPay.prototype.fetchCapabilities = function (options, errback) {
var client = this._options.client;
var cardNumber = options.cardNumber;
if (!cardNumber) {
errback(new BraintreeError({
type: BraintreeError.types.MERCHANT,
message: 'A card number is required'
}));
return;
}
client.request({
method: 'get',
endpoint: 'payment_methods/credit_cards/capabilities',
data: {
_meta: {source: 'unionpay'},
creditCard: {
number: cardNumber
}
}
}, function (err, response) {
if (err) {
errback(new BraintreeError({
type: BraintreeError.types.NETWORK,
message: 'Fetch capabilities network error',
details: {
originalError: err
}
}));
analytics.sendEvent(client, 'web.unionpay.capabilities-failed');
return;
}
analytics.sendEvent(client, 'web.unionpay.capabilities-received');
errback(null, response);
});
};
/**
*/
/**
* @typedef {object} UnionPay~enrollPayload
* @property {string} unionPayEnrollmentId UnionPay enrollment ID
*/
/**
* Enrolls a UnionPay card. Only call this method if the card needs to be enrolled. Use {@link UnionPay#fetchCapabilities|fetchCapabilities} to determine if the user's card needs to be enrolled.
* @public
* @param {object} options UnionPay enrollment options
* @param {object} options.card The card to enroll
* @param {string} options.card.number Card number
* @param {string} options.card.expirationMonth The card's expiration month
* @param {string} options.card.expirationYear The card's expiration year
* @param {string} options.card.mobileCountryCode Customer's mobile country code
* @param {string} options.card.mobileNumber Customer's mobile phone number. This is the mobile phone number UnionPay will send an SMS auth code to
* @param {errback} callback The second argument, <code>data</code>, is a {@link UnionPay~enrollPayload|enrollPayload}
* @returns {void}
*/
UnionPay.prototype.enroll = function (options, callback) {
var client = this._options.client;
var card = options.card;
var data = {
_meta: {source: 'unionpay'},
unionPayEnrollment: {
number: card.number,
expirationMonth: card.expirationMonth,
expirationYear: card.expirationYear,
mobileCountryCode: card.mobileCountryCode,
mobileNumber: card.mobileNumber
}
};
if (this._merchantAccountId) {
data.merchantAccountId = this._merchantAccountId;
}
client.request({
method: 'post',
endpoint: 'union_pay_enrollments',
data: data
}, function (err, response) {
var message;
if (err) {
// TODO: We cannot get response codes, so we can't know whose "fault" this error is.
// This requires a new feature of braintree-request which we are waiting on.
if (err.type === BraintreeError.types.CUSTOMER) {
message = 'Enrollment invalid due to customer input error';
} else {
err.type = BraintreeError.types.NETWORK;
message = 'Enrollment network error';
}
analytics.sendEvent(client, 'web.unionpay.enrollment-failed');
callback(new BraintreeError({
type: err.type,
message: message,
details: {
originalError: err
}
}));
return;
}
analytics.sendEvent(client, 'web.unionpay.enrollment-succeeded');
callback(null, {unionPayEnrollmentId: response.unionPayEnrollmentId});
});
};
/**
* @typedef {object} UnionPay~tokenizePayload
* @property {string} nonce The payment method nonce
* @property {string} type Always <code>CreditCard</code>
* @property {object} details Additional account details
* @property {string} details.cardType Type of card, ex: Visa, MasterCard
* @property {string} details.lastTwo Last two digits of card number
* @property {string} description A human-readable description
*/
/**
* Tokenizes a UnionPay card, returning a nonce payload!
* @public
* @param {object} options UnionPay tokenization options
* @param {object} options.card The card to enroll
* @param {string} options.card.number Card number
* @param {string} options.card.expirationMonth The card's expiration month
* @param {string} options.card.expirationYear The card's expiration year
* @param {string} options.card.cvv The card's security number
* @param {object} options.options Additional options
* @param {string} options.options.id The enrollment id if {@link UnionPay#enroll} was required
* @param {string} options.options.smsCode The SMS code recieved from the user if {@link UnionPay#enroll} was required
* @param {errback} callback The second argument, <code>data</code>, is a {@link UnionPay~tokenizePayload|tokenizePayload}
* @returns {void}
*/
UnionPay.prototype.tokenize = function (options, callback) {
var tokenizedCard;
var client = this._options.client;
client.request({
method: 'post',
endpoint: 'payment_methods/credit_cards',
data: {
_meta: {source: 'unionpay'},
creditCard: {
number: options.card.number,
expirationMonth: options.card.expirationMonth,
expirationYear: options.card.expirationYear,
cvv: options.card.cvv
},
options: options.options
}
}, function (err, response) {
if (err) {
analytics.sendEvent(client, 'web.unionpay.nonce-failed');
// TODO: We cannot get response codes, so we can't know whose "fault" this error is.
// This requires a new feature of braintree-request which we are waiting on.
callback(new BraintreeError({
type: BraintreeError.types.NETWORK,
message: 'Tokenization network error',
details: {
originalError: err
}
}));
return;
}
tokenizedCard = response.creditCards[0];
delete tokenizedCard.consumed;
delete tokenizedCard.threeDSecureInfo;
delete tokenizedCard.type;
analytics.sendEvent(client, 'web.unionpay.nonce-received');
callback(null, tokenizedCard);
});
};
module.exports = UnionPay;