diff --git a/lib/infrastructure/helpers.js b/lib/infrastructure/helpers.js index 8311625..7bed463 100644 --- a/lib/infrastructure/helpers.js +++ b/lib/infrastructure/helpers.js @@ -12,6 +12,20 @@ function defNn(v) { return v !== undefined && v !== null; }; +function isEmptyObject(o){ + if (o === null){ + return true; + } + if (typeof(o)==='object'&&o.length===0){ + return true; + } + if (JSON.stringify(o)==="{}"){ + return true; + } + return false; +} + + var Helpers = { isNumeric: isNumeric, @@ -20,6 +34,8 @@ var Helpers = { defNn: defNn, + isEmptyObject: isEmptyObject, + hydrateCardData: function hydrateCardData(obj) { var result = {}; @@ -39,6 +55,21 @@ var Helpers = { return undefined; }, + hydrateAdditionalTxnFields: + function hydrateAdditionalTxnFields(obj){ + var result = {}; + if (typeof(obj)==='string'){ + obj = {'description':obj}; + } + if (obj) { + if (objProp(obj,'description')) { result.Description = obj.description; } + if (objProp(obj,'invoiceId')) { result.InvoiceNbr = obj.invoiceId; } + if (objProp(obj,'customerId')) { result.CustomerId = obj.customerId; } + return result; + } + return undefined; + }, + hydrateCardHolderData: function hydrateCardHolderData(cardHolder) { var result = {}; @@ -94,7 +125,7 @@ var Helpers = { return 'Refund'; case 'CreditReversal': return 'Reverse'; - case 'creditAuth': + case 'CreditAuth': return 'Authorize'; case 'CreditAccountVerify': return 'Verify'; @@ -110,6 +141,12 @@ var Helpers = { return 'ManageTokens'; case 'SecurityError': return 'SecurityError'; + case 'ReportBatchDetail': + return 'BatchDetail'; + case 'ReportBatchHistory': + return 'BatchHistory'; + case 'ReportBatchSummary': + return 'BatchSummary'; default: return null; } @@ -142,6 +179,12 @@ var Helpers = { return 'BatchClose'; case 'SecurityError': return "SecurityError"; + case 'BatchDetail': + return 'ReportBatchDetail'; + case 'BatchHistory': + return 'ReportBatchHistory'; + case 'BatchSummary': + return 'ReportBatchSummary'; default: return ''; } diff --git a/lib/services/secure-submit/hps-credit-service.js b/lib/services/secure-submit/hps-credit-service.js index c2efd1c..1a5c939 100644 --- a/lib/services/secure-submit/hps-credit-service.js +++ b/lib/services/secure-submit/hps-credit-service.js @@ -50,23 +50,27 @@ function HpsCreditService(hpsConfig, soapUri) { * @return {Object} exports for chaining */ var chargeWithCard = - function chargeWithCard(amount, currency, card, cardHolder, requestMultiUseToken, memo, callback) { + function chargeWithCard(amount, currency, card, cardHolder, requestMultiUseToken, additionalTxnFields, callback) { var schema = porticoSchema.requestType('CreditSale'), tx = {}; if (hlp.defNn(card)) { tx.CardData = {}; - tx.CardData.ManualEntry = hlp.hydrateCardData(card); + if (typeof(card)==='string'){ + tx.CardData.TrackData = card; + } else { + tx.CardData.ManualEntry = hlp.hydrateCardData(card); + schema.properties.CardData.required = ['ManualEntry']; + schema.properties.CardData.properties.ManualEntry.required = ['CardNbr', 'ExpMonth', 'ExpYear', 'CVV2']; + } if(hlp.defNn(requestMultiUseToken)) tx.CardData.TokenRequest = (requestMultiUseToken === true) ? 'Y': 'N'; } - if (hlp.defNn(amount)) tx.Amt = amount; - if (hlp.defNn(cardHolder)) tx.CardHolderData = hlp.hydrateCardHolderData(cardHolder); - if (hlp.defNn(memo)) tx.AdditionalTxnFields = { 'Description': memo }; + if (hlp.defNn(amount)) tx.Amt = amount; + if (hlp.defNn(cardHolder)) tx.CardHolderData = hlp.hydrateCardHolderData(cardHolder); + if (hlp.defNn(additionalTxnFields)) tx.AdditionalTxnFields = hlp.hydrateAdditionalTxnFields(additionalTxnFields); tx.AllowDup = 'Y'; schema.required = ['Amt']; - schema.properties.CardData.required = ['ManualEntry']; - schema.properties.CardData.properties.ManualEntry.required = ['CardNbr', 'ExpMonth', 'ExpYear', 'CVV2']; if (tv4.validate(tx, schema)) { gateway.submitTransaction({'CreditSale':{'Block1':tx}}, function (err, result) { @@ -191,23 +195,27 @@ function HpsCreditService(hpsConfig, soapUri) { * @return {Object} exports for chaining */ var authorizeWithCard = - function authorizeWithCard(amount, currency, card, cardHolder, requestMultiUseToken, memo, callback) { + function authorizeWithCard(amount, currency, card, cardHolder, requestMultiUseToken, additionalTxnFields, callback) { var schema = porticoSchema.requestType('CreditAuth'), tx = {}; if (hlp.defNn(card)) { tx.CardData = {}; - tx.CardData.ManualEntry = hlp.hydrateCardData(card); + if (typeof(card)==='string'){ + tx.CardData.TrackData = card; + } else { + tx.CardData.ManualEntry = hlp.hydrateCardData(card); + schema.properties.CardData.required = ['ManualEntry']; + schema.properties.CardData.properties.ManualEntry.required = ['CardNbr', 'ExpMonth', 'ExpYear', 'CVV2']; + } if(hlp.defNn(requestMultiUseToken)) tx.CardData.TokenRequest = (requestMultiUseToken === true) ? 'Y': 'N'; } - if (hlp.defNn(amount)) tx.Amt = amount; - if (hlp.defNn(cardHolder)) tx.CardHolderData = hlp.hydrateCardHolderData(cardHolder); - if (hlp.defNn(memo)) tx.AdditionalTxnFields = { 'Description': memo }; + if (hlp.defNn(amount)) tx.Amt = amount; + if (hlp.defNn(cardHolder)) tx.CardHolderData = hlp.hydrateCardHolderData(cardHolder); + if (hlp.defNn(additionalTxnFields)) tx.AdditionalTxnFields = hlp.hydrateAdditionalTxnFields(additionalTxnFields); tx.AllowDup = 'Y'; schema.required = ['Amt']; - schema.properties.CardData.required = ['ManualEntry']; - schema.properties.CardData.properties.ManualEntry.required = ['CardNbr', 'ExpMonth', 'ExpYear']; if (tv4.validate(tx, schema)) { gateway.submitTransaction({'CreditAuth':{'Block1':tx}}, function (err, result) { @@ -859,10 +867,11 @@ function HpsCreditService(hpsConfig, soapUri) { transactionId: t.GatewayTxnId, originalTransactionId: t.OriginalGatewayTxnId, maskedCardNumber: t.MaskedCardNbr, - responseCode: t.IssuerRspCode, - responseText: t.IssuerRspText, + responseCode: hlp.isEmptyObject(t.IssuerRspCode)?(hlp.isEmptyObject(t.GatewayRspCode)?'':t.GatewayRspCode):t.IssuerRspCode, + responseText: hlp.isEmptyObject(t.IssuerRspText)?(hlp.isEmptyObject(t.GatewayRspMsg)?'':t.GatewayRspMsg):t.IssuerRspText, amount: t.Amt, settlementAmount: t.SettlementAmt, + status: t.Status, transactionUtcDate: t.TxnUtcDT, transactionType: filterBy || hlp.serviceNameToTransactionType(t.ServiceName), exceptions: (t.GatewayRspCode !== '0' || t.IssuerRspCode !== '00') ? { @@ -948,7 +957,7 @@ function HpsCreditService(hpsConfig, soapUri) { result = { transactionId: t.GatewayTxnId, originalTransactionId: t.OriginalGatewayTxnId, - settlementAmount: t.SettlementAmt, + settlementAmount: t.Data.SettlementAmt, authorizedAmount: t.Data.AuthAmt, authorizationCode: t.Data.AuthCode, avsResultCode: t.Data.AVSRsltCode, diff --git a/test/portico-services/helpers-test.js b/test/portico-services/helpers-test.js new file mode 100644 index 0000000..387440a --- /dev/null +++ b/test/portico-services/helpers-test.js @@ -0,0 +1,83 @@ +'use strict'; + +var assert = require('assert'), + schema = require('../../lib/infrastructure/validation/portico-schema'), + helpers = require('../../lib/infrastructure/helpers'); + +exports.isEmptyObject = { + onNull: function () { + var rc = helpers.isEmptyObject(null); + assert.equal(rc,true,'null should return true'); + }, + + onEmpty: function(){ + var rc = helpers.isEmptyObject({}); + assert.equal(rc,true,'{} should return true'); + }, + + onNotObject: function() { + var rc = helpers.isEmptyObject(''); + assert.equal(rc,false,'\'\' should return false'); + }, + + onString: function(){ + var rc = helpers.isEmptyObject(' '); + assert.equal(rc,false,'\' \' should return false'); + }, + + onNotEmpty: function(){ + var rc = helpers.isEmptyObject({x:0}); + assert.equal(rc,false,'{x:0} should return false'); + } +}; + +exports.validateRequestTypes = { + validObjects: function(){ + requestTypes.map(rt=>{ + var rc = schema.requestType(rt); + assert.equal(helpers.isEmptyObject(rc),false, rt + ' should be a valid type'); + assert.equal(isRequestObject(rc),true, rt + ' should be a valid request object'); + }); + }, + validTransactions: function(){ + requestTypes.map(rt=>{ + var rc = helpers.serviceNameToTransactionType(rt); + assert.notEqual(rc,null, rt + ' should have a Transaction Type'); + }); + }, + + validServiceNames: function(){ + requestTypes.map(rt=>{ + var rc = helpers.serviceNameToTransactionType(rt); + var rc2 = helpers.transactionTypeToServiceName(rc); + assert.notEqual(rc2,null,rt + ' should be a valid Service Name'); + assert.equal(rt,rc2, rt + ' should be the same as ' + rc2); + }); + } +}; + +function isRequestObject(o){ + var rc = false; + if (!helpers.isEmptyObject(o)){ + if (o.type==='object'&&!helpers.isEmptyObject(o.properties)){ + rc = true; + } + } + return rc; +} + +var requestTypes = [ + 'CreditSale', + 'CreditAuth', + 'CreditAccountVerify', + 'CreditAddToBatch', + 'CreditReturn', + 'CreditReversal', + 'ReportActivity', + 'ReportBatchDetail', + 'ReportBatchHistory', + 'ReportBatchSummary', + //'ReportOpenAuths', + 'ReportTxnDetail', + 'ManageTokens' +] \ No newline at end of file