cybersource-soap-node-sdk
Version:
Unofficial Node.js SDK for CyberSource SOAP API with P12 certificate support
452 lines (442 loc) • 15.6 kB
JavaScript
const Configuration = require('./model/Configuration');
const RunTransaction = require('./api/RunTransaction');
const Models = require('./model/index');
/**
* CyberSource SOAP API Client
*
* This class provides a Node.js interface to the CyberSource SOAP API for payment processing.
* It supports both username/password authentication and P12 certificate authentication.
*
* @class CyberSourceApi
* @example
* // Initialize with username/password
* const api = new CyberSourceApi(
* 'your-password',
* 'your-merchant-id',
* 'development',
* 'en',
* '1.151',
* 'USD'
* );
*
* @example
* // Initialize with P12 certificate
* const api = new CyberSourceApi(
* '', // empty password when using certificates
* 'your-merchant-id',
* 'development',
* 'en',
* '1.151',
* 'USD',
* {
* p12Path: '/path/to/certificate.p12',
* p12Passphrase: 'your-passphrase'
* }
* );
*
* @example
* // Initialize with PEM certificates
* const api = new CyberSourceApi(
* '', // empty password when using certificates
* 'your-merchant-id',
* 'development',
* 'en',
* '1.151',
* 'USD',
* {
* privateKeyPath: '/path/to/private-key.pem',
* publicCertPath: '/path/to/certificate.pem',
* passphrase: 'optional-passphrase'
* }
* );
*/
module.exports = class CyberSourceApi {
/**
* Create a CyberSource API client
*
* @param {string} password - CyberSource password (can be empty if using P12 certificates)
* @param {string} merchantID - CyberSource merchant ID
* @param {string} environment - Environment ('development' for sandbox, 'production' for live)
* @param {string} language - Response language ('en' for English, 'es' for Spanish)
* @param {string} version - CyberSource API version (e.g., '1.151')
* @param {string} currency - Default currency code (e.g., 'USD', 'EUR')
* @param {Object} [certOptions] - Certificate options for P12 or PEM authentication
* @param {string} [certOptions.p12Path] - Path to P12 certificate file
* @param {string} [certOptions.p12Base64] - P12 certificate as base64 string
* @param {string} [certOptions.p12Passphrase] - Passphrase for P12 certificate
* @param {string} [certOptions.privateKeyPath] - Path to private key PEM file
* @param {string} [certOptions.publicCertPath] - Path to public certificate PEM file
* @param {string} [certOptions.passphrase] - Passphrase for private key
*/
constructor(password, merchantID, environment, language, version, currency, certOptions = null) {
this.configuration = new Configuration(
password,
merchantID,
environment,
language,
version,
currency,
certOptions
);
this.Models = new Models(version);
this.ErrorObject = new this.Models.ErrorObject(language);
}
/**
* Get the configuration object
*
* @returns {Configuration} The configuration object containing API settings
*/
getConfiguration() {
return this.configuration;
}
/**
* Send a generic request to CyberSource
*
* This is a low-level method that can be used for custom requests.
* Most users should use the specific methods like authorizeCharge, chargeCard, etc.
*
* @param {Object} request_object - The request object to send to CyberSource
* @returns {Promise<Object>} Promise that resolves with the response or rejects with an error
* @example
* const customRequest = {
* merchantID: 'your-merchant-id',
* merchantReferenceCode: 'REF123',
* // ... other request fields
* };
* const result = await api.normalRequest(customRequest);
*/
normalRequest(request_object) {
return new Promise((resolve, reject) => {
RunTransaction(this.configuration.url, request_object, this.configuration.getWsSecurity())
.then(response => {
if (response.reasonCode === 100) {
resolve({
message: this.ErrorObject.getMessage(response.reasonCode),
code: response.reasonCode,
data: response || '',
});
} else {
reject({
message: this.ErrorObject.getMessage(response.reasonCode),
code: response.reasonCode,
data: response,
});
}
})
.catch(error => {
reject({
message: this.ErrorObject.getMessage(500),
code: 500,
data: error,
});
});
});
}
/**
* Authorize a charge (without capturing it)
*
* This method authorizes a payment but does not capture it. You'll need to call
* captureCharge separately to complete the transaction.
*
* @param {AuthorizationRequest} authorizationRequest - The authorization request object
* @param {number} amount - The amount to authorize
* @returns {Promise<Object>} Promise that resolves with authorization details
* @throws {Object} Error object with code, message, and data properties
* @example
* const billTo = new api.Models.BillTo('John', 'Doe', '123 St', 'City', 'CA', '12345', 'US', 'john@test.com');
* const card = new api.Models.Card('4111111111111111', '12', '2025', '123');
* const authRequest = new api.Models.AuthorizationRequest('REF123', billTo, card);
*
* try {
* const result = await api.authorizeCharge(authRequest, 100.00);
* console.log('Authorization ID:', result.authorization);
* } catch (error) {
* console.error('Authorization failed:', error.message);
* }
*/
authorizeCharge(authorizationRequest, amount) {
return new Promise((resolve, reject) => {
authorizationRequest.purchaseTotals = new this.Models.PurchaseTotals(
this.configuration.currency,
amount.toFixed(2) + ''
);
authorizationRequest.merchantID = this.configuration.merchantID;
RunTransaction(
this.configuration.url,
authorizationRequest.getJSONWithAttributes(this.configuration.merchantID),
this.configuration.getWsSecurity()
)
.then(res => {
if (res.reasonCode == 100) {
if (res.ccAuthReply.reasonCode == 100) {
resolve({
message: this.ErrorObject.getMessage(res.ccAuthReply.reasonCode),
code: res.ccAuthReply.reasonCode,
authorization: res.requestID,
});
} else {
reject({
message: this.ErrorObject.getMessage(res.ccAuthReply.reasonCode),
code: res.ccAuthReply.reasonCode,
data: res.ccAuthReply,
});
}
} else {
reject({
message: this.ErrorObject.getMessage(res.reasonCode),
code: res.reasonCode,
data: res,
});
}
})
.catch(e => {
reject({
message: this.ErrorObject.getMessage(500),
code: 500,
data: e,
});
});
});
}
/**
* Capture a previously authorized charge
*
* @param {CaptureRequest} captureRequest - The capture request object
* @param {number} amount - The amount to capture (should not exceed authorized amount)
* @returns {Promise<Object>} Promise that resolves with capture confirmation
* @throws {Object} Error object with code, message, and data properties
* @example
* const captureRequest = new api.Models.CaptureRequest('REF123', authorizationId);
* const result = await api.captureCharge(captureRequest, 100.00);
*/
captureCharge(captureRequest, amount) {
return new Promise((resolve, reject) => {
captureRequest.purchaseTotals = new this.Models.PurchaseTotals(
this.configuration.currency,
amount.toFixed(2) + ''
);
captureRequest.merchantID = this.configuration.merchantID;
RunTransaction(
this.configuration.url,
captureRequest.getJSON(),
this.configuration.getWsSecurity()
)
.then(res => {
if (res.reasonCode == 100) {
resolve({
message: this.ErrorObject.getMessage(res.reasonCode),
code: res.reasonCode,
});
} else {
reject({
message: this.ErrorObject.getMessage(res.reasonCode),
code: res.reasonCode,
data: res,
});
}
})
.catch(e => {
reject({
message: this.ErrorObject.getMessage(500),
code: 500,
data: e,
});
});
});
}
/**
* Create a subscription (tokenize a card) for future use
*
* @param {SubscriptionRequest} subscriptionRequest - The subscription request object
* @returns {Promise<Object>} Promise that resolves with subscription token
* @throws {Object} Error object with code, message, and data properties
* @example
* const subscriptionRequest = new api.Models.SubscriptionRequest('SUB123', billTo, card);
* const result = await api.subscribeCard(subscriptionRequest);
* console.log('Token:', result.token);
*/
subscribeCard(subscriptionRequest) {
return new Promise((resolve, reject) => {
subscriptionRequest.purchaseTotals = new this.Models.PurchaseTotals(
this.configuration.currency,
subscriptionRequest.amount ? subscriptionRequest.amount.toString() : '0.00'
);
subscriptionRequest.merchantID = this.configuration.merchantID;
RunTransaction(
this.configuration.url,
subscriptionRequest.getJSONWithAttributes(this.configuration.merchantID),
this.configuration.getWsSecurity()
)
.then(res => {
if (res.reasonCode == 100) {
resolve({
message: this.ErrorObject.getMessage(res.reasonCode),
code: res.reasonCode,
token: res.requestID,
});
} else {
reject({
message: this.ErrorObject.getMessage(res.reasonCode),
code: res.reasonCode,
data: res,
});
}
})
.catch(e => {
reject({
message: this.ErrorObject.getMessage(500),
code: 500,
data: e,
});
});
});
}
/**
* Get subscription information by subscription ID
*
* @param {SubscriptionInfoRequest} subscriptionInfoRequest - The subscription info request object
* @returns {Promise<Object>} Promise that resolves with subscription information
* @throws {Object} Error object with code, message, and data properties
* @example
* const subscriptionInfoRequest = new api.Models.SubscriptionInfoRequest('SUB-123456');
* const result = await api.getSubscriptionInfo(subscriptionInfoRequest);
* console.log('Subscription Info:', result);
*/
getSubscriptionInfo(subscriptionInfoRequest) {
return new Promise((resolve, reject) => {
// Set the xmlns to match the API version
subscriptionInfoRequest.xmlns = this.Models.xmlns;
subscriptionInfoRequest.merchantID = this.configuration.merchantID;
RunTransaction(
this.configuration.url,
subscriptionInfoRequest.getJSONWithAttributes(this.configuration.merchantID),
this.configuration.getWsSecurity()
)
.then(res => {
if (res.reasonCode == 100) {
resolve({
message: this.ErrorObject.getMessage(res.reasonCode),
code: res.reasonCode,
subscriptionInfo: res,
});
} else {
reject({
message: this.ErrorObject.getMessage(res.reasonCode),
code: res.reasonCode,
data: res,
});
}
})
.catch(e => {
reject({
message: this.ErrorObject.getMessage(500),
code: 500,
data: e,
});
});
});
}
/**
* Charge a previously subscribed card using its token
*
* @param {ChargeSubscriptionRequest} chargeSubscriptionRequest - The charge subscription request object
* @param {number} amount - The amount to charge
* @returns {Promise<Object>} Promise that resolves with charge confirmation
* @throws {Object} Error object with code, message, and data properties
* @example
* const chargeSubRequest = new api.Models.ChargeSubscriptionRequest('CHARGE123', token);
* const result = await api.chargeSubscribedCard(chargeSubRequest, 50.00);
*/
chargeSubscribedCard(chargeSubscriptionRequest, amount) {
return new Promise((resolve, reject) => {
chargeSubscriptionRequest.purchaseTotals = new this.Models.PurchaseTotals(
this.configuration.currency,
amount.toFixed(2) + ''
);
chargeSubscriptionRequest.merchantID = this.configuration.merchantID;
RunTransaction(
this.configuration.url,
chargeSubscriptionRequest.getJSON(),
this.configuration.getWsSecurity()
)
.then(res => {
if (res.reasonCode == 100) {
resolve({
message: this.ErrorObject.getMessage(res.reasonCode),
code: res.reasonCode,
});
} else {
reject({
message: this.ErrorObject.getMessage(res.reasonCode),
code: res.reasonCode,
data: res,
});
}
})
.catch(e => {
reject({
message: this.ErrorObject.getMessage(500),
code: 500,
data: e,
});
});
});
}
/**
* Charge a card directly (authorization + capture in one step)
*
* This is the most commonly used method for processing payments.
* It combines authorization and capture into a single transaction.
*
* @param {ChargeRequest} chargeRequest - The charge request object
* @param {number} amount - The amount to charge
* @returns {Promise<Object>} Promise that resolves with charge confirmation
* @throws {Object} Error object with code, message, and data properties
* @example
* const billTo = new api.Models.BillTo('John', 'Doe', '123 St', 'City', 'CA', '12345', 'US', 'john@test.com');
* const card = new api.Models.Card('4111111111111111', '12', '2025', '123');
* const chargeRequest = new api.Models.ChargeRequest('REF123', billTo, card);
*
* try {
* const result = await api.chargeCard(chargeRequest, 100.00);
* console.log('Charge successful:', result.message);
* } catch (error) {
* console.error('Charge failed:', error.message);
* }
*/
chargeCard(chargeRequest, amount) {
return new Promise((resolve, reject) => {
chargeRequest.purchaseTotals = new this.Models.PurchaseTotals(
this.configuration.currency,
amount.toFixed(2) + ''
);
chargeRequest.merchantID = this.configuration.merchantID;
RunTransaction(
this.configuration.url,
chargeRequest.getJSON(),
this.configuration.getWsSecurity()
)
.then(res => {
if (res.reasonCode == 100) {
resolve({
message: this.ErrorObject.getMessage(res.reasonCode),
code: res.reasonCode,
});
} else {
reject({
message: this.ErrorObject.getMessage(res.reasonCode),
code: res.reasonCode,
data: res,
});
}
})
.catch(e => {
reject({
message: this.ErrorObject.getMessage(500),
code: 500,
data: e,
});
});
});
}
};