diff --git a/examples/3ds-example.ts b/examples/3ds-example.ts new file mode 100644 index 0000000..8b9cf5c --- /dev/null +++ b/examples/3ds-example.ts @@ -0,0 +1,143 @@ +import express from 'express'; +import AzulAPI from '../src/azul-api/api'; +import { ChallengeIndicator } from '../src/azul-api/secure/types'; +import 'dotenv/config'; + +const app = express(); +app.use(express.urlencoded({ extended: true })); + +const CARDS = [ + { + value: '4149011500000519', + label: '3D Secure Method con desafío' + }, + { + value: '4265880000000007', + label: 'Sin fricción con 3DSMethod' + }, + { + value: '4147463011110117', + label: 'Sin fricción sin 3DSMethod' + }, + { + value: '4005520000000129', + label: 'Desafío con 3DSMethod' + }, + { + value: '4147463011110059', + label: 'Desafío sin 3DSMethod' + }, + { + value: '4149011500000527', + label: 'Desafío' + } +]; + +const azul = new AzulAPI({ + auth1: process.env.AUTH1!, + auth2: process.env.AUTH2!, + merchantId: process.env.MERCHANT_ID!, + certificatePath: process.env.CERTIFICATE_PATH!, + keyPath: process.env.KEY_PATH! +}); + +app.get('/', (req, res) => { + res.send( + ` +

3DS Example

+

Choose a card

+ ${CARDS.map( + (card) => `` + ).join('

')} + ` + ); +}); + +app.get('/buy', async (req, res) => { + const cardNumber = req.query.cardNumber; + + if (typeof cardNumber !== 'string') { + return res.status(400).send('Invalid card number'); + } + + const result = await azul.secure.sale({ + cardNumber, + expiration: '202412', + CVC: '818', + customOrderId: '1234', + amount: 1000, + ITBIS: 100, + browserInfo: { + AcceptHeader: + 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signedexchange;v=b3;q=0.9', + IPAddress: '127.0.0.1', + Language: 'en-US', + ColorDepth: '24', + ScreenWidth: '2880', + ScreenHeight: '1800', + TimeZone: '240', + UserAgent: + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36', + JavaScriptEnabled: true + }, + cardHolderInfo: { + BillingAddressCity: 'Ciudad Facturación', + BillingAddressCountry: 'País Facturación', + BillingAddressLine1: 'Línea 1 Dirección Facturación', + BillingAddressLine2: 'Línea 2 Dirección Facturación', + BillingAddressLine3: 'Línea 3 Dirección Facturación', + BillingAddressState: 'Estado o Provincia Facturación', + BillingAddressZip: '99999', + Email: 'correo@dominio.com', + Name: 'Nombre Tarjetahabiente', + PhoneHome: '8099999999', + PhoneMobile: '8299999999', + PhoneWork: '8499999999', + ShippingAddressCity: 'Ciudad Envío', + ShippingAddressCountry: 'País Envío', + ShippingAddressLine1: 'Línea 1 Dirección Envío', + ShippingAddressLine2: 'Línea 2 Dirección Envío', + ShippingAddressLine3: 'Línea 3 Dirección Envío', + ShippingAddressState: 'Estado o Provincia Facturación', + ShippingAddressZip: '99999' + }, + threeDSAuth: { + TermUrl: 'http://localhost:3000/post-3ds', + MethodNotificationUrl: 'http://localhost:3000/capture-3ds', + RequestorChallengeIndicator: ChallengeIndicator.NO_PREFERENCE + } + }); + + if (result.ok) { + res.send(result.value); + } else { + if (result.value.IsoCode === '00') { + res.send(result.value); + } else { + res.status(500).send('Error'); + } + } +}); + +app.post('/post-3ds', async (req, res) => { + const { id } = req.query; + const { cres } = req.body; + + if (typeof id !== 'string' || typeof cres !== 'string') { + return res.status(400).send('Invalid ID'); + } + + res.send(await azul.secure.post3DS(id, cres)); +}); + +app.post('/capture-3ds', async (req, res) => { + const id = req.query.id; + + if (typeof id !== 'string') { + return res.status(400).send('Invalid ID'); + } + + res.send(await azul.secure.capture3DS(id)); +}); + +app.listen(3000); diff --git a/package-lock.json b/package-lock.json index a416475..7ac40d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5258,6 +5258,7 @@ }, "node_modules/npm/node_modules/@isaacs/cliui": { "version": "8.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5274,6 +5275,7 @@ }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { "version": "6.0.1", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5285,11 +5287,13 @@ }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5306,6 +5310,7 @@ }, "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5320,11 +5325,13 @@ }, "node_modules/npm/node_modules/@isaacs/string-locale-compare": { "version": "1.1.0", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/@npmcli/agent": { "version": "2.2.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5340,6 +5347,7 @@ }, "node_modules/npm/node_modules/@npmcli/arborist": { "version": "7.5.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5387,6 +5395,7 @@ }, "node_modules/npm/node_modules/@npmcli/config": { "version": "8.3.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5405,6 +5414,7 @@ }, "node_modules/npm/node_modules/@npmcli/fs": { "version": "3.1.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5416,6 +5426,7 @@ }, "node_modules/npm/node_modules/@npmcli/git": { "version": "5.0.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5434,6 +5445,7 @@ }, "node_modules/npm/node_modules/@npmcli/installed-package-contents": { "version": "2.1.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5449,6 +5461,7 @@ }, "node_modules/npm/node_modules/@npmcli/map-workspaces": { "version": "3.0.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5463,6 +5476,7 @@ }, "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { "version": "7.1.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5478,6 +5492,7 @@ }, "node_modules/npm/node_modules/@npmcli/name-from-folder": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5486,6 +5501,7 @@ }, "node_modules/npm/node_modules/@npmcli/node-gyp": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5494,6 +5510,7 @@ }, "node_modules/npm/node_modules/@npmcli/package-json": { "version": "5.1.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5511,6 +5528,7 @@ }, "node_modules/npm/node_modules/@npmcli/promise-spawn": { "version": "7.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5522,6 +5540,7 @@ }, "node_modules/npm/node_modules/@npmcli/query": { "version": "3.1.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5533,6 +5552,7 @@ }, "node_modules/npm/node_modules/@npmcli/redact": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5541,6 +5561,7 @@ }, "node_modules/npm/node_modules/@npmcli/run-script": { "version": "8.1.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5557,6 +5578,7 @@ }, "node_modules/npm/node_modules/@pkgjs/parseargs": { "version": "0.11.0", + "dev": true, "inBundle": true, "license": "MIT", "optional": true, @@ -5566,6 +5588,7 @@ }, "node_modules/npm/node_modules/@sigstore/bundle": { "version": "2.3.1", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -5577,6 +5600,7 @@ }, "node_modules/npm/node_modules/@sigstore/core": { "version": "1.1.0", + "dev": true, "inBundle": true, "license": "Apache-2.0", "engines": { @@ -5585,6 +5609,7 @@ }, "node_modules/npm/node_modules/@sigstore/protobuf-specs": { "version": "0.3.1", + "dev": true, "inBundle": true, "license": "Apache-2.0", "engines": { @@ -5593,6 +5618,7 @@ }, "node_modules/npm/node_modules/@sigstore/sign": { "version": "2.3.0", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -5607,6 +5633,7 @@ }, "node_modules/npm/node_modules/@sigstore/tuf": { "version": "2.3.2", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -5619,6 +5646,7 @@ }, "node_modules/npm/node_modules/@sigstore/verify": { "version": "1.2.0", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -5632,6 +5660,7 @@ }, "node_modules/npm/node_modules/@tufjs/canonical-json": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5640,6 +5669,7 @@ }, "node_modules/npm/node_modules/@tufjs/models": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5652,6 +5682,7 @@ }, "node_modules/npm/node_modules/abbrev": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5660,6 +5691,7 @@ }, "node_modules/npm/node_modules/agent-base": { "version": "7.1.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5671,6 +5703,7 @@ }, "node_modules/npm/node_modules/aggregate-error": { "version": "3.1.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5683,6 +5716,7 @@ }, "node_modules/npm/node_modules/ansi-regex": { "version": "5.0.1", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5691,6 +5725,7 @@ }, "node_modules/npm/node_modules/ansi-styles": { "version": "6.2.1", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5702,21 +5737,25 @@ }, "node_modules/npm/node_modules/aproba": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/archy": { "version": "1.0.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/balanced-match": { "version": "1.0.2", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/bin-links": { "version": "4.0.3", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5731,6 +5770,7 @@ }, "node_modules/npm/node_modules/binary-extensions": { "version": "2.3.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5742,6 +5782,7 @@ }, "node_modules/npm/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5750,6 +5791,7 @@ }, "node_modules/npm/node_modules/builtins": { "version": "5.1.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5758,6 +5800,7 @@ }, "node_modules/npm/node_modules/cacache": { "version": "18.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5780,6 +5823,7 @@ }, "node_modules/npm/node_modules/chalk": { "version": "5.3.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5791,6 +5835,7 @@ }, "node_modules/npm/node_modules/chownr": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5799,6 +5844,7 @@ }, "node_modules/npm/node_modules/ci-info": { "version": "4.0.0", + "dev": true, "funding": [ { "type": "github", @@ -5813,6 +5859,7 @@ }, "node_modules/npm/node_modules/cidr-regex": { "version": "4.0.5", + "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -5824,6 +5871,7 @@ }, "node_modules/npm/node_modules/clean-stack": { "version": "2.2.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5832,6 +5880,7 @@ }, "node_modules/npm/node_modules/cli-columns": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5844,6 +5893,7 @@ }, "node_modules/npm/node_modules/cmd-shim": { "version": "6.0.2", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -5852,6 +5902,7 @@ }, "node_modules/npm/node_modules/color-convert": { "version": "2.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5863,16 +5914,19 @@ }, "node_modules/npm/node_modules/color-name": { "version": "1.1.4", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/common-ancestor-path": { "version": "1.0.1", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/cross-spawn": { "version": "7.0.3", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5886,6 +5940,7 @@ }, "node_modules/npm/node_modules/cross-spawn/node_modules/which": { "version": "2.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -5900,6 +5955,7 @@ }, "node_modules/npm/node_modules/cssesc": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "MIT", "bin": { @@ -5911,6 +5967,7 @@ }, "node_modules/npm/node_modules/debug": { "version": "4.3.4", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -5927,11 +5984,13 @@ }, "node_modules/npm/node_modules/debug/node_modules/ms": { "version": "2.1.2", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/diff": { "version": "5.2.0", + "dev": true, "inBundle": true, "license": "BSD-3-Clause", "engines": { @@ -5940,16 +5999,19 @@ }, "node_modules/npm/node_modules/eastasianwidth": { "version": "0.2.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/emoji-regex": { "version": "8.0.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/encoding": { "version": "0.1.13", + "dev": true, "inBundle": true, "license": "MIT", "optional": true, @@ -5959,6 +6021,7 @@ }, "node_modules/npm/node_modules/env-paths": { "version": "2.2.1", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5967,16 +6030,19 @@ }, "node_modules/npm/node_modules/err-code": { "version": "2.0.3", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/exponential-backoff": { "version": "3.1.1", + "dev": true, "inBundle": true, "license": "Apache-2.0" }, "node_modules/npm/node_modules/fastest-levenshtein": { "version": "1.0.16", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -5985,6 +6051,7 @@ }, "node_modules/npm/node_modules/foreground-child": { "version": "3.1.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6000,6 +6067,7 @@ }, "node_modules/npm/node_modules/fs-minipass": { "version": "3.0.3", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6011,6 +6079,7 @@ }, "node_modules/npm/node_modules/function-bind": { "version": "1.1.2", + "dev": true, "inBundle": true, "license": "MIT", "funding": { @@ -6019,6 +6088,7 @@ }, "node_modules/npm/node_modules/glob": { "version": "10.3.12", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6040,11 +6110,13 @@ }, "node_modules/npm/node_modules/graceful-fs": { "version": "4.2.11", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/hasown": { "version": "2.0.2", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6056,6 +6128,7 @@ }, "node_modules/npm/node_modules/hosted-git-info": { "version": "7.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6067,11 +6140,13 @@ }, "node_modules/npm/node_modules/http-cache-semantics": { "version": "4.1.1", + "dev": true, "inBundle": true, "license": "BSD-2-Clause" }, "node_modules/npm/node_modules/http-proxy-agent": { "version": "7.0.2", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6084,6 +6159,7 @@ }, "node_modules/npm/node_modules/https-proxy-agent": { "version": "7.0.4", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6096,6 +6172,7 @@ }, "node_modules/npm/node_modules/iconv-lite": { "version": "0.6.3", + "dev": true, "inBundle": true, "license": "MIT", "optional": true, @@ -6108,6 +6185,7 @@ }, "node_modules/npm/node_modules/ignore-walk": { "version": "6.0.4", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6119,6 +6197,7 @@ }, "node_modules/npm/node_modules/imurmurhash": { "version": "0.1.4", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -6127,6 +6206,7 @@ }, "node_modules/npm/node_modules/indent-string": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -6135,6 +6215,7 @@ }, "node_modules/npm/node_modules/ini": { "version": "4.1.2", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6143,6 +6224,7 @@ }, "node_modules/npm/node_modules/init-package-json": { "version": "6.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6160,6 +6242,7 @@ }, "node_modules/npm/node_modules/ip-address": { "version": "9.0.5", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6172,6 +6255,7 @@ }, "node_modules/npm/node_modules/ip-regex": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -6183,6 +6267,7 @@ }, "node_modules/npm/node_modules/is-cidr": { "version": "5.0.5", + "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -6194,6 +6279,7 @@ }, "node_modules/npm/node_modules/is-core-module": { "version": "2.13.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6205,6 +6291,7 @@ }, "node_modules/npm/node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -6213,16 +6300,19 @@ }, "node_modules/npm/node_modules/is-lambda": { "version": "1.0.1", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/isexe": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/jackspeak": { "version": "2.3.6", + "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -6240,11 +6330,13 @@ }, "node_modules/npm/node_modules/jsbn": { "version": "1.1.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/json-parse-even-better-errors": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -6253,6 +6345,7 @@ }, "node_modules/npm/node_modules/json-stringify-nice": { "version": "1.1.4", + "dev": true, "inBundle": true, "license": "ISC", "funding": { @@ -6261,6 +6354,7 @@ }, "node_modules/npm/node_modules/jsonparse": { "version": "1.3.1", + "dev": true, "engines": [ "node >= 0.2.0" ], @@ -6269,16 +6363,19 @@ }, "node_modules/npm/node_modules/just-diff": { "version": "6.0.2", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/just-diff-apply": { "version": "5.5.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/libnpmaccess": { "version": "8.0.5", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6291,6 +6388,7 @@ }, "node_modules/npm/node_modules/libnpmdiff": { "version": "6.1.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6309,6 +6407,7 @@ }, "node_modules/npm/node_modules/libnpmexec": { "version": "8.1.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6329,6 +6428,7 @@ }, "node_modules/npm/node_modules/libnpmfund": { "version": "5.0.9", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6340,6 +6440,7 @@ }, "node_modules/npm/node_modules/libnpmhook": { "version": "10.0.4", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6352,6 +6453,7 @@ }, "node_modules/npm/node_modules/libnpmorg": { "version": "6.0.5", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6364,6 +6466,7 @@ }, "node_modules/npm/node_modules/libnpmpack": { "version": "7.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6378,6 +6481,7 @@ }, "node_modules/npm/node_modules/libnpmpublish": { "version": "9.0.7", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6396,6 +6500,7 @@ }, "node_modules/npm/node_modules/libnpmsearch": { "version": "7.0.4", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6407,6 +6512,7 @@ }, "node_modules/npm/node_modules/libnpmteam": { "version": "6.0.4", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6419,6 +6525,7 @@ }, "node_modules/npm/node_modules/libnpmversion": { "version": "6.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6434,6 +6541,7 @@ }, "node_modules/npm/node_modules/lru-cache": { "version": "10.2.2", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6442,6 +6550,7 @@ }, "node_modules/npm/node_modules/make-fetch-happen": { "version": "13.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6464,6 +6573,7 @@ }, "node_modules/npm/node_modules/minimatch": { "version": "9.0.4", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6478,6 +6588,7 @@ }, "node_modules/npm/node_modules/minipass": { "version": "7.0.4", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6486,6 +6597,7 @@ }, "node_modules/npm/node_modules/minipass-collect": { "version": "2.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6497,6 +6609,7 @@ }, "node_modules/npm/node_modules/minipass-fetch": { "version": "3.0.4", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6513,6 +6626,7 @@ }, "node_modules/npm/node_modules/minipass-flush": { "version": "1.0.5", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6524,6 +6638,7 @@ }, "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { "version": "3.3.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6535,6 +6650,7 @@ }, "node_modules/npm/node_modules/minipass-json-stream": { "version": "1.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6544,6 +6660,7 @@ }, "node_modules/npm/node_modules/minipass-json-stream/node_modules/minipass": { "version": "3.3.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6555,6 +6672,7 @@ }, "node_modules/npm/node_modules/minipass-pipeline": { "version": "1.2.4", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6566,6 +6684,7 @@ }, "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { "version": "3.3.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6577,6 +6696,7 @@ }, "node_modules/npm/node_modules/minipass-sized": { "version": "1.0.3", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6588,6 +6708,7 @@ }, "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { "version": "3.3.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6599,6 +6720,7 @@ }, "node_modules/npm/node_modules/minizlib": { "version": "2.1.2", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6611,6 +6733,7 @@ }, "node_modules/npm/node_modules/minizlib/node_modules/minipass": { "version": "3.3.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6622,6 +6745,7 @@ }, "node_modules/npm/node_modules/mkdirp": { "version": "1.0.4", + "dev": true, "inBundle": true, "license": "MIT", "bin": { @@ -6633,11 +6757,13 @@ }, "node_modules/npm/node_modules/ms": { "version": "2.1.3", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/mute-stream": { "version": "1.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6646,6 +6772,7 @@ }, "node_modules/npm/node_modules/negotiator": { "version": "0.6.3", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -6654,6 +6781,7 @@ }, "node_modules/npm/node_modules/node-gyp": { "version": "10.1.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6677,6 +6805,7 @@ }, "node_modules/npm/node_modules/node-gyp/node_modules/proc-log": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6685,6 +6814,7 @@ }, "node_modules/npm/node_modules/nopt": { "version": "7.2.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6699,6 +6829,7 @@ }, "node_modules/npm/node_modules/normalize-package-data": { "version": "6.0.0", + "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -6713,6 +6844,7 @@ }, "node_modules/npm/node_modules/npm-audit-report": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6721,6 +6853,7 @@ }, "node_modules/npm/node_modules/npm-bundled": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6732,6 +6865,7 @@ }, "node_modules/npm/node_modules/npm-install-checks": { "version": "6.3.0", + "dev": true, "inBundle": true, "license": "BSD-2-Clause", "dependencies": { @@ -6743,6 +6877,7 @@ }, "node_modules/npm/node_modules/npm-normalize-package-bin": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6751,6 +6886,7 @@ }, "node_modules/npm/node_modules/npm-package-arg": { "version": "11.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6765,6 +6901,7 @@ }, "node_modules/npm/node_modules/npm-packlist": { "version": "8.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6776,6 +6913,7 @@ }, "node_modules/npm/node_modules/npm-pick-manifest": { "version": "9.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6790,6 +6928,7 @@ }, "node_modules/npm/node_modules/npm-profile": { "version": "9.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6802,6 +6941,7 @@ }, "node_modules/npm/node_modules/npm-registry-fetch": { "version": "17.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6820,6 +6960,7 @@ }, "node_modules/npm/node_modules/npm-user-validate": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "BSD-2-Clause", "engines": { @@ -6828,6 +6969,7 @@ }, "node_modules/npm/node_modules/p-map": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6842,6 +6984,7 @@ }, "node_modules/npm/node_modules/pacote": { "version": "18.0.3", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6872,6 +7015,7 @@ }, "node_modules/npm/node_modules/parse-conflict-json": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6885,6 +7029,7 @@ }, "node_modules/npm/node_modules/path-key": { "version": "3.1.1", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -6893,6 +7038,7 @@ }, "node_modules/npm/node_modules/path-scurry": { "version": "1.10.2", + "dev": true, "inBundle": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -6908,6 +7054,7 @@ }, "node_modules/npm/node_modules/postcss-selector-parser": { "version": "6.0.16", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6920,6 +7067,7 @@ }, "node_modules/npm/node_modules/proc-log": { "version": "4.2.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6928,6 +7076,7 @@ }, "node_modules/npm/node_modules/proggy": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -6936,6 +7085,7 @@ }, "node_modules/npm/node_modules/promise-all-reject-late": { "version": "1.0.1", + "dev": true, "inBundle": true, "license": "ISC", "funding": { @@ -6944,6 +7094,7 @@ }, "node_modules/npm/node_modules/promise-call-limit": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "ISC", "funding": { @@ -6952,11 +7103,13 @@ }, "node_modules/npm/node_modules/promise-inflight": { "version": "1.0.1", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/promise-retry": { "version": "2.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -6969,6 +7122,7 @@ }, "node_modules/npm/node_modules/promzard": { "version": "1.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6980,6 +7134,7 @@ }, "node_modules/npm/node_modules/qrcode-terminal": { "version": "0.12.0", + "dev": true, "inBundle": true, "bin": { "qrcode-terminal": "bin/qrcode-terminal.js" @@ -6987,6 +7142,7 @@ }, "node_modules/npm/node_modules/read": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -6998,6 +7154,7 @@ }, "node_modules/npm/node_modules/read-cmd-shim": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -7006,6 +7163,7 @@ }, "node_modules/npm/node_modules/read-package-json-fast": { "version": "3.0.2", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7018,6 +7176,7 @@ }, "node_modules/npm/node_modules/retry": { "version": "0.12.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -7026,12 +7185,14 @@ }, "node_modules/npm/node_modules/safer-buffer": { "version": "2.1.2", + "dev": true, "inBundle": true, "license": "MIT", "optional": true }, "node_modules/npm/node_modules/semver": { "version": "7.6.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7046,6 +7207,7 @@ }, "node_modules/npm/node_modules/semver/node_modules/lru-cache": { "version": "6.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7057,6 +7219,7 @@ }, "node_modules/npm/node_modules/shebang-command": { "version": "2.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7068,6 +7231,7 @@ }, "node_modules/npm/node_modules/shebang-regex": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -7076,6 +7240,7 @@ }, "node_modules/npm/node_modules/signal-exit": { "version": "4.1.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -7087,6 +7252,7 @@ }, "node_modules/npm/node_modules/sigstore": { "version": "2.3.0", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -7103,6 +7269,7 @@ }, "node_modules/npm/node_modules/smart-buffer": { "version": "4.2.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -7112,6 +7279,7 @@ }, "node_modules/npm/node_modules/socks": { "version": "2.8.3", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7125,6 +7293,7 @@ }, "node_modules/npm/node_modules/socks-proxy-agent": { "version": "8.0.3", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7138,6 +7307,7 @@ }, "node_modules/npm/node_modules/spdx-correct": { "version": "3.2.0", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -7147,6 +7317,7 @@ }, "node_modules/npm/node_modules/spdx-correct/node_modules/spdx-expression-parse": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7156,11 +7327,13 @@ }, "node_modules/npm/node_modules/spdx-exceptions": { "version": "2.5.0", + "dev": true, "inBundle": true, "license": "CC-BY-3.0" }, "node_modules/npm/node_modules/spdx-expression-parse": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7170,16 +7343,19 @@ }, "node_modules/npm/node_modules/spdx-license-ids": { "version": "3.0.17", + "dev": true, "inBundle": true, "license": "CC0-1.0" }, "node_modules/npm/node_modules/sprintf-js": { "version": "1.1.3", + "dev": true, "inBundle": true, "license": "BSD-3-Clause" }, "node_modules/npm/node_modules/ssri": { "version": "10.0.5", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7191,6 +7367,7 @@ }, "node_modules/npm/node_modules/string-width": { "version": "4.2.3", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7205,6 +7382,7 @@ "node_modules/npm/node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7218,6 +7396,7 @@ }, "node_modules/npm/node_modules/strip-ansi": { "version": "6.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7230,6 +7409,7 @@ "node_modules/npm/node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7241,6 +7421,7 @@ }, "node_modules/npm/node_modules/supports-color": { "version": "9.4.0", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -7252,6 +7433,7 @@ }, "node_modules/npm/node_modules/tar": { "version": "6.2.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7268,6 +7450,7 @@ }, "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { "version": "2.1.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7279,6 +7462,7 @@ }, "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { "version": "3.3.6", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7290,6 +7474,7 @@ }, "node_modules/npm/node_modules/tar/node_modules/minipass": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -7298,16 +7483,19 @@ }, "node_modules/npm/node_modules/text-table": { "version": "0.2.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/tiny-relative-date": { "version": "1.3.0", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/treeverse": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -7316,6 +7504,7 @@ }, "node_modules/npm/node_modules/tuf-js": { "version": "2.2.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7329,6 +7518,7 @@ }, "node_modules/npm/node_modules/unique-filename": { "version": "3.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7340,6 +7530,7 @@ }, "node_modules/npm/node_modules/unique-slug": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7351,11 +7542,13 @@ }, "node_modules/npm/node_modules/util-deprecate": { "version": "1.0.2", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/validate-npm-package-license": { "version": "3.0.4", + "dev": true, "inBundle": true, "license": "Apache-2.0", "dependencies": { @@ -7365,6 +7558,7 @@ }, "node_modules/npm/node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7374,6 +7568,7 @@ }, "node_modules/npm/node_modules/validate-npm-package-name": { "version": "5.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7385,11 +7580,13 @@ }, "node_modules/npm/node_modules/walk-up-path": { "version": "3.0.1", + "dev": true, "inBundle": true, "license": "ISC" }, "node_modules/npm/node_modules/which": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7404,6 +7601,7 @@ }, "node_modules/npm/node_modules/which/node_modules/isexe": { "version": "3.1.1", + "dev": true, "inBundle": true, "license": "ISC", "engines": { @@ -7412,6 +7610,7 @@ }, "node_modules/npm/node_modules/wrap-ansi": { "version": "8.1.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7429,6 +7628,7 @@ "node_modules/npm/node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7445,6 +7645,7 @@ }, "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { "version": "4.3.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7459,6 +7660,7 @@ }, "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "6.0.1", + "dev": true, "inBundle": true, "license": "MIT", "engines": { @@ -7470,11 +7672,13 @@ }, "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { "version": "9.2.2", + "dev": true, "inBundle": true, "license": "MIT" }, "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { "version": "5.1.2", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7491,6 +7695,7 @@ }, "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "7.1.0", + "dev": true, "inBundle": true, "license": "MIT", "dependencies": { @@ -7505,6 +7710,7 @@ }, "node_modules/npm/node_modules/write-file-atomic": { "version": "5.0.1", + "dev": true, "inBundle": true, "license": "ISC", "dependencies": { @@ -7517,6 +7723,7 @@ }, "node_modules/npm/node_modules/yallist": { "version": "4.0.0", + "dev": true, "inBundle": true, "license": "ISC" }, diff --git a/src/azul-api/api.ts b/src/azul-api/api.ts index ca457a9..f40dfec 100644 --- a/src/azul-api/api.ts +++ b/src/azul-api/api.ts @@ -10,17 +10,20 @@ import DataVault from './data-vault/data-vault'; import ProcessPayment from './process-payment/process-payment'; import { ProcessPaymentResponse } from './process-payment/schemas'; import { Process } from './processes'; +import { Secure } from './secure/secure'; class AzulAPI { private requester: AzulRequester; public valut: DataVault; public payments: ProcessPayment; + public secure: Secure; constructor(config: Config) { this.requester = new AzulRequester(config); this.valut = new DataVault(this.requester); this.payments = new ProcessPayment(this.requester); + this.secure = new Secure(this.requester); } /** diff --git a/src/azul-api/data-vault/shemas.ts b/src/azul-api/data-vault/shemas.ts index 03fb13e..b8ad989 100644 --- a/src/azul-api/data-vault/shemas.ts +++ b/src/azul-api/data-vault/shemas.ts @@ -9,7 +9,6 @@ import { customOrderId, customerServicePhone, expiration, - forceNo3DS, orderNumber, posInputMode } from '../schemas'; @@ -149,14 +148,7 @@ export const DataVaultSaleSchema = z.object({ * depende de lo conversado con Negocios SDP. Si es * MOTO la transacción, no se debería enviar CVC. */ - dataVaultToken: z.string().max(100), - /** - * Valores posibles 0 =no, 1 = Si. Si se envía el valor en - * ‘0’, la transacción se procesa con 3D Secure. Si se - * envía el valor en ‘1’ la transacción se procesa sin - * 3D Secure. - */ - forceNo3DS + dataVaultToken: z.string().max(100) }); export type DataVaultSaleInput = z.input; diff --git a/src/azul-api/process-payment/schemas.ts b/src/azul-api/process-payment/schemas.ts index 53905ce..3026274 100644 --- a/src/azul-api/process-payment/schemas.ts +++ b/src/azul-api/process-payment/schemas.ts @@ -12,7 +12,6 @@ import { customerServicePhone, dataVaultToken, expiration, - forceNo3DS, orderNumber, posInputMode, saveToDataVault @@ -126,14 +125,7 @@ export const ProcessPaymentSchema = z.object({ * valor en 1, SDP le devolverá el token generado en * el campo DataVaultToken */ - saveToDataVault, - /** - * Valores posibles 0 =no, 1 = Si. Si se envía el valor en - * ‘0’, la transacción se procesa con 3D Secure. Si se - * envía el valor en ‘1’ la transacción se procesa sin - * 3D Secure. - */ - forceNo3DS + saveToDataVault }); export const RefundRequestSchema = z @@ -316,13 +308,6 @@ export const RefundSchema = z.object({ * el campo DataVaultToken */ saveToDataVault, - /** - * Valores posibles 0 =no, 1 = Si. Si se envía el valor en - * ‘0’, la transacción se procesa con 3D Secure. Si se - * envía el valor en ‘1’ la transacción se procesa sin - * 3D Secure. - */ - forceNo3DS, /** * Número de orden Azul. Puede ser usado en vez del RRN para generar una * devolución. Importante dar prioridad a este valor sobre el RRN. diff --git a/src/azul-api/processes.ts b/src/azul-api/processes.ts index d30dce2..66a3684 100644 --- a/src/azul-api/processes.ts +++ b/src/azul-api/processes.ts @@ -3,5 +3,7 @@ export enum Process { Post = 'ProcessPost', Datavault = 'ProcessDatavault', VerifyPayment = 'VerifyPayment', - SearchPayments = 'SearchPayments' + SearchPayments = 'SearchPayments', + Process3DsMethod = 'ProcessThreedsMethod', + Process3DsChallenge = 'ProcessThreedsChallenge' } diff --git a/src/azul-api/request.ts b/src/azul-api/request.ts index bd618c5..688a0ca 100644 --- a/src/azul-api/request.ts +++ b/src/azul-api/request.ts @@ -64,6 +64,12 @@ class AzulRequester { const { cert, key } = await this.getCertificates(); + const requestBody = capitalizeKeys({ + channel: this.channel, + store: this.merchantId, + ...body + }); + const response = await request(url, { method: 'POST', headers: { @@ -77,13 +83,7 @@ class AzulRequester { key } }), - body: JSON.stringify( - capitalizeKeys({ - channel: this.channel, - store: this.merchantId, - ...body - }) - ) + body: JSON.stringify(requestBody) }); return (await response.body.json()) as any; diff --git a/src/azul-api/secure/secure.ts b/src/azul-api/secure/secure.ts new file mode 100644 index 0000000..db9d6e0 --- /dev/null +++ b/src/azul-api/secure/secure.ts @@ -0,0 +1,186 @@ +import { randomUUID } from 'crypto'; +import { ProcessPaymentTransaction } from '../process-payment/process-payment'; +import { ProcessPaymentSchema, ProcessPaymentSchemaInput } from '../process-payment/schemas'; +import { Process } from '../processes'; +import AzulRequester from '../request'; +import { BrowserInfo, CardHolderInfo, MethodNotificationStatus, ThreeDSAuth } from './types'; +import { sleep } from '../../utils'; + +export class Secure { + private readonly requester: AzulRequester; + private secureIdToAzulOrderId = new Map(); + + constructor(requester: AzulRequester) { + this.requester = requester; + } + + async sale( + input: ProcessPaymentSchemaInput & { + cardHolderInfo: CardHolderInfo; + browserInfo: BrowserInfo; + threeDSAuth: ThreeDSAuth; + } & { + useIframe?: boolean; + } + ): Promise< + | { + ok: true; + value: string; + } + | { + ok: false; + value: any; + } + > { + const secureId = randomUUID(); + + const result = await this.requester.safeRequest({ + ...ProcessPaymentSchema.parse(input), + forceNo3DS: '0', + cardHolderInfo: input.cardHolderInfo, + browserInfo: input.browserInfo, + threeDSAuth: { + ...input.threeDSAuth, + TermUrl: input.threeDSAuth.TermUrl + `?id=${secureId}`, + MethodNotificationUrl: input.threeDSAuth.MethodNotificationUrl + `?id=${secureId}` + }, + trxType: ProcessPaymentTransaction.SALE + }); + + if (result.ResponseMessage === '3D_SECURE_CHALLENGE') { + this.secureIdToAzulOrderId.set(secureId, result.AzulOrderId); + + return { + ok: true, + value: challengeResponse({ + termUrl: input.threeDSAuth.TermUrl, + creq: result.ThreeDSChallenge.CReq, + redirectPostUrl: result.ThreeDSChallenge.RedirectPostUrl + }) + }; + } else if (result.ResponseMessage === '3D_SECURE_2_METHOD') { + this.secureIdToAzulOrderId.set(secureId, result.AzulOrderId); + + let form: string = result.ThreeDSMethod.MethodForm; + + if (!input.useIframe) { + form = form.replace('target=', ''); + } + + return { + ok: true, + value: form + }; + } else { + return { + ok: false, + value: result + }; + } + } + + async process3DS(input: { + azulOrderId: string; + methodNotificationStatus: MethodNotificationStatus; + }) { + return await this.requester.safeRequest( + { + azulOrderId: input.azulOrderId, + methodNotificationStatus: input.methodNotificationStatus + }, + Process.Process3DsMethod + ); + } + + async process3DsChallenge(input: { azulOrderId: string; cRes: string }) { + return await this.requester.safeRequest( + { + azulOrderId: input.azulOrderId, + cRes: input.cRes + }, + Process.Process3DsChallenge + ); + } + + async post3DS(id: string, cRes: string) { + const azulOrderId = this.secureIdToAzulOrderId.get(id); + + if (typeof azulOrderId !== 'string') { + throw new Error('Invalid ID'); + } + + return await this.process3DsChallenge({ + azulOrderId, + cRes + }); + } + + async capture3DS(id: string): Promise { + if (!this.secureIdToAzulOrderId.has(id)) { + throw new Error('Invalid ID'); + } + + const process3DResult = await this.get3DResult(id); + + if (process3DResult.ResponseMessage === '3D_SECURE_CHALLENGE') { + return challengeResponse({ + creq: process3DResult.ThreeDSChallenge.CReq, + termUrl: 'http://localhost:3000/post-3ds?id=' + id, + redirectPostUrl: process3DResult.ThreeDSChallenge.RedirectPostUrl + }); + } + + return process3DResult; + } + + private processResult = new Map(); + private processLoading = new Map(); + + private async get3DResult(id: string) { + // If we already have the result, return it + if (this.processResult.has(id)) { + return this.processResult.get(id); + } + + // If we are already processing the result, wait for it to finish + if (this.processLoading.get(id)) { + while (this.processLoading.get(id)) { + await sleep(100); + } + + return this.processResult.get(id); + } + + // Otherwise, start processing the result + this.processLoading.set(id, true); + const result = await this.process3DS({ + azulOrderId: this.secureIdToAzulOrderId.get(id)!, + methodNotificationStatus: MethodNotificationStatus.RECEIVED + }); + this.processLoading.set(id, false); + + this.processResult.set(id, result); + return result; + } +} + +function challengeResponse({ + creq, + termUrl, + redirectPostUrl +}: { + creq: string; + termUrl: string; + redirectPostUrl: string; +}) { + return ` +
+ + + +
`; +} diff --git a/src/azul-api/secure/types.ts b/src/azul-api/secure/types.ts new file mode 100644 index 0000000..8a4c2cf --- /dev/null +++ b/src/azul-api/secure/types.ts @@ -0,0 +1,250 @@ +export enum ChallengeIndicator { + /** + * Sin preferencias (no tiene preferencia si se debe realizar un desafío. + * Este es el valor predeterminado) + */ + NO_PREFERENCE = '01', + + /** + * No solicitar ningún desafío (comercio prefiere que no se realice ningún desafío). + */ + NO_CHALLENGE = '02', + + /** + * Solicitar desafío: Preferencia del solicitante de 3DS (prefiere que se realice un + * desafío; esto debe establecerse para transacciones de alto riesgo o valor) + */ + CHALLENGE = '03', + + /** + * Cuando solicitar desafío es mandatorio (existen mandatos locales o regionales + * que indican que se debe realizar un desafío, actualmente en República + * Dominicana no es mandatorio por lo que en este momento este valor no aplica + * para nuestro país). + */ + MANDATORY_CHALLENGE = '04' +} + +export enum MethodNotificationStatus { + /** + * Ha enviado el elemento methodNotificationURL en la + * solicitud de transacción de venta inicial y ha recibido la + * notificación de ACS en 10 segundos, recibirá un mensaje HTTP + * POST de ACS, que contendrá un identificador de transacción + * único representado por threeDSServerTransID + */ + RECEIVED = 'RECEIVED', + /** + * Ha enviado el elemento methodNotificationURL en la + * solicitud de transacción de venta inicial y no ha recibido la + * notificación de ACS en 10 segundos + */ + EXPECTED_BUT_NOT_RECEIVED = 'EXPECTED_BUT_NOT_RECEIVED', + /** + * No ha enviado el elemento methodNotificationURL en la + * solicitud de transacción de venta inicial + */ + NOT_EXPECTED = 'NOT_EXPECTED' +} + +export type ThreeDSAuth = { + /** + * URL del comercio donde serán posteados los valores de + * respuesta con el resultado de la autenticación por el servidor + * ACS (este es el servidor del banco emisor que procesa la + * autenticación del tarjetahabiente). Se deber construir con un + * identificador único que sirva para asociar + */ + TermUrl: string; + + /** + * URL del comercio donde se recibirá la notificación de que se + * completó el iFrame (el cual se hará referencia más adelante) y + * capturó los datos del navegador para ser usados en el análisis de + * riesgo. + * + * Esta notificación debe llegar mediante un HTTP Post del + * servidor ACS del emisor y contiene un identificador único de + * transacción representado con el threeDSServerTransID. Esta + * URL debe ser única e identificable, por lo que cuando se reciba + * la notificación, debería poder asociarse con la transacción + * correspondiente. Esto elimina cualquier dependencia del + * threeDSServerTransID, que se recibe con la respuesta del nodo + * ThreeDSMethod. + * + * Una forma sencilla de garantizar el mapeo correcto con las + * transacciones es pasar una referencia de transacción como un + * query string. Por ejemplo: + * “http://www.mitienda.com/3dscapture.aspx?sid=637195280075507073" + */ + MethodNotificationUrl: string; + + /** + * Este indicador sirve para comunicar al banco emisor la + * preferencia que tiene comercio de que se solicite el desafío + */ + RequestorChallengeIndicator: ChallengeIndicator; +}; + +export type CardHolderInfo = { + /** + * Ciudad de la dirección de facturación + * Máximo 96 caracteres, incluyendo espacios + */ + BillingAddressCity: string; + + /** + * País de la dirección de facturación + * Código ISO de 2 caracteres del país + */ + BillingAddressCountry: string; + + /** + * Dirección de facturación – Línea 1 + * Máximo 96 caracteres, incluyendo espacios + */ + BillingAddressLine1: string; + + /** + * Dirección de facturación – Línea 2 + * Máximo 96 caracteres, incluyendo espacios + */ + BillingAddressLine2: string; + + /** + * Dirección de facturación – Línea 3 + * Máximo 96 caracteres, incluyendo espacios + */ + BillingAddressLine3: string; + + /** + * Estado o provincia de la dirección de facturación + * Máximo 96 caracteres, incluyendo espacios + */ + BillingAddressState: string; + + /** + * Código postal o “ZIP code” de la dirección de facturación + * Máximo 24 caracteres, incluyendo espacios + */ + BillingAddressZip: string; + + /** + * Dirección de correo electrónico + * Máximo 254 caracteres + */ + Email: string; + + /** + * Nombre tarjetahabiente + * Máximo 96 caracteres, incluyendo espacios + */ + Name: string; + + /** + * Teléfono de la casa + * Máximo 32 caracteres + */ + PhoneHome: string; + + /** + * Teléfono móvil + * Máximo 32 caracteres + */ + PhoneMobile: string; + + /** + * Teléfono del trabajo + * Máximo 32 caracteres + */ + PhoneWork: string; + + /** + * Ciudad de la dirección de envío + * Máximo 96 caracteres, incluyendo espacios + */ + ShippingAddressCity: string; + + /** + * País de la dirección de envío + * Código ISO de 2 caracteres del país + */ + ShippingAddressCountry: string; + + /** + * Dirección de envío – Línea 1 + * Máximo 96 caracteres, incluyendo espacios + */ + ShippingAddressLine1: string; + + /** + * Dirección de envío – Línea 2 + * Máximo 96 caracteres, incluyendo espacios + */ + ShippingAddressLine2: string; + + /** + * Dirección de envío – Línea 3 + * Máximo 96 caracteres, incluyendo espacios + */ + ShippingAddressLine3: string; + + /** + * Estado o provincia de la dirección de envío + * Máximo 96 caracteres, incluyendo espacios + */ + ShippingAddressState: string; + + /** + * Código postal o “ZIP code” de la dirección de envío + * Máximo 24 caracteres, incluyendo espacios + */ + ShippingAddressZip: string; +}; + +export type BrowserInfo = { + /** + * Contiene los tipos de contenido que el cliente puede aceptar, ordenados por la calidad del contenido. + */ + AcceptHeader: string; + + /** + * Dirección IP del cliente. + */ + IPAddress: string; + + /** + * Idioma preferido del cliente, generalmente el idioma del navegador. + */ + Language: string; + + /** + * Profundidad de color de la pantalla del cliente. + */ + ColorDepth: string; + + /** + * Ancho de la pantalla del cliente. + */ + ScreenWidth: string; + + /** + * Alto de la pantalla del cliente. + */ + ScreenHeight: string; + + /** + * Zona horaria del cliente. + */ + TimeZone: string; + + /** + * Cadena que identifica al navegador del cliente. + */ + UserAgent: string; + + /** + * Indica si JavaScript está habilitado en el navegador del cliente. + */ + JavaScriptEnabled: boolean; +}; diff --git a/src/utils.ts b/src/utils.ts index 576c30f..0ab6750 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -22,3 +22,9 @@ export function capitalizeKeys(obj: any): any { export type Prettify = { [K in keyof T]: T[K]; } & {}; + +export function sleep(ms: number) { + return new Promise((resolve) => { + setTimeout(resolve, ms); + }); +}