{"_id":"@diegomax/payway-ar-ts","_rev":"2-d5f04396edbdbc6ef28919a16bdbb406","name":"@diegomax/payway-ar-ts","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@diegomax/payway-ar-ts","version":"0.1.0","license":"MIT","_id":"@diegomax/payway-ar-ts@0.1.0","maintainers":[{"name":"diego.massanti","email":"diego@massanti.com"}],"homepage":"https://github.com/DiegoMax/payway-ar-ts#readme","bugs":{"url":"https://github.com/DiegoMax/payway-ar-ts/issues"},"dist":{"shasum":"9f4aeb394d61adb00df5dc9198a24e33ab6e5938","tarball":"https://registry.npmjs.org/@diegomax/payway-ar-ts/-/payway-ar-ts-0.1.0.tgz","fileCount":46,"integrity":"sha512-stZgl4rRk5G9rZHHoHG/JRcoIgtzBqVGr0SORxweF+bsGnr0a0FHB65nPCnOmvUjNiMTV3SL7hUqceUd8l1XcA==","signatures":[{"sig":"MEUCICrfWFnlYIOVUMme1miHmdw562ZLuvnriemLUH9ULUqwAiEA5BZE0U0EsncMXuXT3I8mdat1hCE6a7AOHEKEaNMGDNg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":119248},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"340fe77589ffe434d197f8e4d67727ef225b4cf2","scripts":{"docs":"typedoc","test":"vitest run","build":"tsc -p tsconfig.build.json","prepare":"npm run build","typecheck":"tsc -p tsconfig.json --noEmit","test:watch":"vitest"},"_npmUser":{"name":"diego.massanti","email":"diego@massanti.com"},"repository":{"url":"git+https://github.com/DiegoMax/payway-ar-ts.git","type":"git"},"_npmVersion":"11.9.0","description":"SDK TypeScript para Payway AR en Node 22+.","directories":{},"_nodeVersion":"22.22.0","dependencies":{"zod":"^3.24.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.1.2","typedoc":"^0.28.2","typescript":"^5.8.3","@types/node":"^22.14.1"},"_npmOperationalInternal":{"tmp":"tmp/payway-ar-ts_0.1.0_1776210245993_0.1691371936888404","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@diegomax/payway-ar-ts","version":"0.2.0","description":"SDK TypeScript para Payway AR en Node 22+.","repository":{"type":"git","url":"git+https://github.com/DiegoMax/payway-ar-ts.git"},"publishConfig":{"access":"public"},"license":"MIT","type":"module","engines":{"node":">=22"},"main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc -p tsconfig.build.json","typecheck":"tsc -p tsconfig.json --noEmit","test":"vitest run","test:watch":"vitest","docs":"typedoc","prepare":"npm run build"},"dependencies":{"zod":"^3.24.3"},"devDependencies":{"@types/node":"^22.14.1","typedoc":"^0.28.2","typescript":"^5.8.3","vitest":"^3.1.2"},"gitHead":"e153bcb23c9a194d5a87537fca1dcb4a88a4d05c","_id":"@diegomax/payway-ar-ts@0.2.0","bugs":{"url":"https://github.com/DiegoMax/payway-ar-ts/issues"},"homepage":"https://github.com/DiegoMax/payway-ar-ts#readme","_nodeVersion":"22.22.0","_npmVersion":"11.9.0","dist":{"integrity":"sha512-8OJB1Y0q8IO9PbKpTdOUZd9MxOqvTfyBVIRAZ2/C151LjC1UtPFIMslw319rbqTfbx6QYmRB2CjVtWgLKuXf3Q==","shasum":"b1a4a0b7e87b2321a46006c3f624b19b21343b88","tarball":"https://registry.npmjs.org/@diegomax/payway-ar-ts/-/payway-ar-ts-0.2.0.tgz","fileCount":47,"unpackedSize":133064,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIG006FnRjc0EKQhJsZsLvzXeVYBuJBDtAn7W9L80FQy6AiBDUpXloBNT3h3wxqEb9NDk6Cb/tSIKdf2TXuqPUrt8Tg=="}]},"_npmUser":{"name":"diego.massanti","email":"diego@massanti.com"},"directories":{},"maintainers":[{"name":"diego.massanti","email":"diego@massanti.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/payway-ar-ts_0.2.0_1776211307772_0.8372919071509115"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-14T23:44:05.875Z","modified":"2026-04-15T00:01:48.021Z","0.1.0":"2026-04-14T23:44:06.161Z","0.2.0":"2026-04-15T00:01:47.909Z"},"bugs":{"url":"https://github.com/DiegoMax/payway-ar-ts/issues"},"license":"MIT","homepage":"https://github.com/DiegoMax/payway-ar-ts#readme","repository":{"type":"git","url":"git+https://github.com/DiegoMax/payway-ar-ts.git"},"description":"SDK TypeScript para Payway AR en Node 22+.","maintainers":[{"name":"diego.massanti","email":"diego@massanti.com"}],"readme":"# @diegomax/payway-ar-ts\n\nSDK TypeScript para Payway AR orientado a Node 22+.\n\nCliente Node moderno para operar con Payway AR usando `fetch` nativo, `AbortController`, validaciones con Zod y tipado estricto en TypeScript.\n\n## Estado\n\nImplementacion inicial en progreso. Esta primera version ya incluye:\n\n- cliente HTTP nativo con timeout por `AbortController`\n- configuracion tipada por entorno\n- operaciones principales de pagos, refunds, tokens, 3DS, batch closure, healthcheck y checkout\n- suite de tests mockeada con Vitest\n- documentacion base en espanol\n\n## Instalacion\n\n```bash\nnpm install @diegomax/payway-ar-ts\n```\n\n## Requisitos\n\n- Node 22 o superior\n- credenciales de Payway para el ambiente correspondiente\n\n## Configuracion\n\n```ts\nimport { PaywayClient } from \"@diegomax/payway-ar-ts\";\n\nconst client = new PaywayClient({\n  environment: \"test\",\n  timeoutMs: 30000,\n  credentials: {\n    privateKey: process.env.PAYWAY_PRIVATE_KEY,\n    publicKey: process.env.PAYWAY_PUBLIC_KEY,\n    formApiKey: process.env.PAYWAY_FORM_API_KEY,\n    formSite: process.env.PAYWAY_FORM_SITE,\n    xConsumerUsername: process.env.PAYWAY_X_CONSUMER_USERNAME,\n  },\n  sourceMetadata: {\n    service: \"mi-backend\",\n    developer: \"mi-equipo\",\n    grouper: \"payments\",\n  },\n});\n```\n\n## Credenciales por dominio\n\n- `privateKey`: pagos, refunds, healthcheck, batch closure, checkout server-side e internal tokenization.\n- `publicKey`: tokenizacion.\n- `formApiKey` y `formSite`: validaciones del flujo checkout.\n- `xConsumerUsername`: operaciones 3DS.\n\n## Uso rapido\n\n```ts\nimport { PaywayClient } from \"@diegomax/payway-ar-ts\";\n\nconst client = new PaywayClient({\n  environment: \"test\",\n  credentials: {\n    privateKey: process.env.PAYWAY_PRIVATE_KEY,\n    publicKey: process.env.PAYWAY_PUBLIC_KEY,\n  },\n});\n\nconst payment = await client.payments.create({\n  site_transaction_id: \"order-1001\",\n  payment_method_id: 1,\n  token: \"sample-token\",\n  bin: \"450799\",\n  amount: 125045,\n  currency: \"ARS\",\n  installments: 1,\n  description: \"Compra de prueba\",\n  payment_type: \"single\",\n});\n\nconsole.log(payment.status);\n```\n\n## Operaciones principales\n\n```ts\nconst status = await client.health.getStatus();\n\nconst paymentInfo = await client.payments.get(\"574421\", {\n  expand: \"card_data\",\n});\n\nconst refund = await client.payments.refund(\"574671\", {});\n\nconst cards = await client.tokens.listCards(\"cliente-123\");\n```\n\n## Ejemplo de pago PCI\n\nEste flujo envia los datos de tarjeta directamente en la transaccion, sin tokenizacion previa. Debe usarse solo desde un backend controlado y con los resguardos PCI correspondientes.\n\n```ts\nimport { PaywayClient } from \"@diegomax/payway-ar-ts\";\n\nconst client = new PaywayClient({\n  environment: \"test\",\n  credentials: {\n    privateKey: process.env.PAYWAY_PRIVATE_KEY,\n  },\n});\n\nconst payment = await client.payments.create({\n  site_transaction_id: \"pci-order-1002\",\n  payment_method_id: 1,\n  amount: 125045,\n  currency: \"ARS\",\n  installments: 1,\n  description: \"Pago PCI sin tokenizacion\",\n  payment_type: \"single\",\n  card_data: {\n    card_number: \"4507990000004905\",\n    card_expiration_month: \"12\",\n    card_expiration_year: \"30\",\n    security_code: \"123\",\n    card_holder_name: \"APRO\",\n    card_holder_identification: {\n      type: \"dni\",\n      number: \"25123456\",\n    },\n  },\n});\n\nconsole.log(payment.status);\n```\n\nEn este escenario no se envian `token` ni `bin`, porque la operacion utiliza `card_data` como fuente primaria de la informacion de pago.\n\n## Ejemplos de pagos offline\n\nPara medios offline se utiliza `client.payments.createOffline()`. Los montos se envian como enteros en centavos y el `payment_method_id` cambia segun el medio:\n\n- `25`: Pago Facil\n- `26`: Rapipago\n- `41`: Pago Mis Cuentas\n- `51`: Cobro Express\n\n### Pago Facil\n\n```ts\nconst pagoFacil = await client.payments.createOffline({\n  site_transaction_id: \"offline-pf-1001\",\n  token: \"92a95793-3321-447c-8795-8aeb8a8ac067\",\n  payment_method_id: 25,\n  amount: 1000,\n  currency: \"ARS\",\n  payment_type: \"single\",\n  email: \"user@mail.com\",\n  invoice_expiration: \"191123\",\n  cod_p3: \"12\",\n  cod_p4: \"134\",\n  client: \"12345678\",\n  surcharge: 1001,\n  payment_mode: \"offline\",\n});\n```\n\n### Rapipago\n\n```ts\nconst rapipago = await client.payments.createOffline({\n  site_transaction_id: \"offline-rp-1002\",\n  token: \"8e190c82-6a63-467e-8a09-9e8fa2ab6215\",\n  payment_method_id: 26,\n  amount: 1000,\n  currency: \"ARS\",\n  payment_type: \"single\",\n  email: \"user@mail.com\",\n  invoice_expiration: \"191123\",\n  cod_p3: \"12\",\n  cod_p4: \"134\",\n  client: \"12345678\",\n  surcharge: 1001,\n  payment_mode: \"offline\",\n});\n```\n\n### Pago Mis Cuentas\n\n```ts\nconst pagoMisCuentas = await client.payments.createOffline({\n  site_transaction_id: \"offline-pmc-1003\",\n  token: \"9ae1d130-8c89-4c3b-a267-0e97b88fedd0\",\n  payment_method_id: 41,\n  amount: 1000,\n  currency: \"ARS\",\n  payment_type: \"single\",\n  email: \"user@mail.com\",\n  bank_id: \"1\",\n  invoice_expiration: \"191123\",\n  payment_mode: \"offline\",\n});\n```\n\n### Cobro Express\n\n```ts\nconst cobroExpress = await client.payments.createOffline({\n  site_transaction_id: \"offline-ce-1004\",\n  token: \"3df26771-67ab-4a8e-91e2-f1e0b0c559f7\",\n  payment_method_id: 51,\n  amount: 1000,\n  currency: \"ARS\",\n  payment_type: \"single\",\n  email: \"user@mail.com\",\n  invoice_expiration: \"191123\",\n  second_invoice_expiration: \"191130\",\n  cod_p3: \"01\",\n  cod_p4: \"134\",\n  client: \"12345678\",\n  surcharge: 1001,\n  payment_mode: \"offline\",\n});\n```\n\nLos ejemplos anteriores asumen que ya existe un `token` valido para la operacion. La respuesta depende del medio y normalmente incluye los datos necesarios para emitir o presentar el cupon de pago.\n\n### Que guardar despues de crear un pago offline\n\nCuando creas un pago offline conviene persistir al menos:\n\n- `site_transaction_id`\n- `id` u `operation_id` devuelto por la API\n- `status`\n- los datos del cupon o referencia de pago que devuelva el medio\n\nEl `id` u `operation_id` es el identificador mas util para consultar luego el estado real de la operacion.\n\n### Como actualizar el estado de un pago offline\n\nLa forma mas directa de actualizar el estado es consultar la operacion con `client.payments.get()` usando el identificador devuelto al crear el pago.\n\n```ts\nconst created = await client.payments.createOffline({\n  site_transaction_id: \"offline-pf-1001\",\n  token: \"92a95793-3321-447c-8795-8aeb8a8ac067\",\n  payment_method_id: 25,\n  amount: 1000,\n  currency: \"ARS\",\n  payment_type: \"single\",\n  email: \"user@mail.com\",\n  invoice_expiration: \"191123\",\n  cod_p3: \"12\",\n  cod_p4: \"134\",\n  client: \"12345678\",\n  surcharge: 1001,\n  payment_mode: \"offline\",\n});\n\nconst operationId = created.operation_id ?? created.id;\n\nif (!operationId) {\n  throw new Error(\"La respuesta no incluyo operation_id ni id\");\n}\n\nconst paymentInfo = await client.payments.get(String(operationId));\n\nconsole.log(paymentInfo.status);\nconsole.log(paymentInfo.status_details);\n```\n\n### Estrategia recomendada para seguimiento\n\nPara pagos offline, lo habitual es:\n\n1. Crear la operacion y mostrar o almacenar el cupon.\n2. Persistir `site_transaction_id`, `id` u `operation_id` y el `status` inicial.\n3. Consultar el estado de la operacion en segundo plano hasta recibir el estado final que necesite tu negocio.\n4. Actualizar tu orden interna solo a partir de la respuesta de Payway.\n\n### Sobre webhooks y notificaciones\n\nLa referencia disponible para esta integracion no documenta un webhook especifico para confirmar pagos offline completados o rechazados. Por eso, en este SDK la recomendacion operativa es basarse en consultas de estado por API.\n\nSi tu cuenta de Payway tiene algun mecanismo de notificacion server-to-server habilitado por configuracion comercial o por otro producto, deberias validarlo con la documentacion oficial de tu cuenta o con soporte de Payway antes de depender de ese flujo.\n\n### Ejemplo de polling simple\n\n```ts\nasync function waitForOfflineResolution(operationId: string, maxAttempts = 20) {\n  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {\n    const payment = await client.payments.get(operationId);\n\n    if (payment.status && payment.status !== \"pending\") {\n      return payment;\n    }\n\n    await new Promise((resolve) => setTimeout(resolve, 30000));\n  }\n\n  throw new Error(\n    \"El pago offline no llego a un estado final dentro de la ventana esperada\",\n  );\n}\n```\n\nEl estado final exacto depende del flujo configurado en Payway y del medio de pago. Si necesitas una clasificacion mas estricta en tu sistema, mapea los valores reales de `payment.status` que recibas en sandbox y produccion.\n\n## Ejemplos de formularios de pago\n\nPara crear un formulario de pago hospedado se utiliza `client.checkout.generateLink()`. Ese endpoint devuelve un `payment_link` que luego puedes redirigir o mostrar al comprador.\n\n### Crear un link de checkout\n\n```ts\nimport { PaywayClient } from \"@diegomax/payway-ar-ts\";\n\nconst client = new PaywayClient({\n  environment: \"test\",\n  credentials: {\n    privateKey: process.env.PAYWAY_PRIVATE_KEY,\n    publicKey: process.env.PAYWAY_PUBLIC_KEY,\n  },\n});\n\nconst checkout = await client.checkout.generateLink({\n  site: \"03101980\",\n  template_id: 1,\n  total_price: 1200,\n  currency: \"ARS\",\n  payment_method_id: 1,\n  installments: [1],\n  payment_description: \"Producto o servicio\",\n  public_apikey: process.env.PAYWAY_PUBLIC_KEY,\n  success_url: \"https://shop.example.com/success\",\n  cancel_url: \"https://shop.example.com/cancel\",\n  redirect_url: \"https://shop.example.com/redirect\",\n  notifications_url: \"https://shop.example.com/payway/notifications\",\n  products: [\n    {\n      id: \"sku-001\",\n      quantity: 1,\n      value: 1200,\n      description: \"Producto o servicio\",\n    },\n  ],\n});\n\nconsole.log(checkout.payment_link);\n```\n\n### Redirigir al comprador al formulario\n\nUna vez obtenido el `payment_link`, el flujo habitual es redirigir al usuario a esa URL:\n\n```ts\nconst { payment_link } = await client.checkout.generateLink({\n  site: \"03101980\",\n  template_id: 1,\n  total_price: 1200,\n  payment_method_id: 1,\n  installments: [1],\n  success_url: \"https://shop.example.com/success\",\n  cancel_url: \"https://shop.example.com/cancel\",\n  public_apikey: process.env.PAYWAY_PUBLIC_KEY,\n});\n\nif (!payment_link) {\n  throw new Error(\"Payway no devolvio payment_link\");\n}\n\nreturn Response.redirect(payment_link, 302);\n```\n\n### Crear un formulario con Cybersource\n\nSi tu cuenta opera con checkout y antifraude Cybersource, utiliza `template_id: 2` y agrega `fraud_detection`.\n\n```ts\nconst checkoutWithCybersource = await client.checkout.generateLink({\n  site: \"03101980\",\n  template_id: 2,\n  total_price: 1200,\n  currency: \"ARS\",\n  payment_method_id: 1,\n  installments: [1],\n  public_apikey: process.env.PAYWAY_PUBLIC_KEY,\n  success_url: \"https://shop.example.com/success\",\n  cancel_url: \"https://shop.example.com/cancel\",\n  fraud_detection: {\n    send_to_cs: true,\n    channel: \"Web\",\n    device_unique_id: \"1234-1234\",\n    bill_to: {\n      city: \"Buenos Aires\",\n      country: \"AR\",\n      customer_id: \"cliente-123\",\n      email: \"buyer@example.com\",\n      first_name: \"Leila\",\n      last_name: \"Sosa\",\n      phone_number: \"1548866329\",\n      postal_code: \"1427\",\n      state: \"BA\",\n      street1: \"Lavalle 4041\",\n    },\n    ship_to: {\n      city: \"Buenos Aires\",\n      country: \"AR\",\n      email: \"buyer@example.com\",\n      first_name: \"Leila\",\n      last_name: \"Sosa\",\n      phone_number: \"1549066329\",\n      postal_code: \"1427\",\n      state: \"BA\",\n      street1: \"Lavalle 4041\",\n    },\n    purchase_totals: {\n      currency: \"ARS\",\n      grandTotalAmount: 1200,\n    },\n    items: [\n      {\n        code: \"sku-001\",\n        description: \"Producto o servicio\",\n        name: \"Producto o servicio\",\n        sku: \"sku-001\",\n        quantity: 1,\n        total_amount: 1200,\n        unit_price: 1200,\n      },\n    ],\n  },\n});\n\nconsole.log(checkoutWithCybersource.payment_link);\n```\n\n### Validar payloads del formulario\n\nSi necesitas validar un payload de checkout antes de usarlo, puedes llamar `client.checkout.validate()`. Este flujo requiere `formApiKey` y `formSite` en las credenciales del cliente.\n\n```ts\nconst validation = await client.checkout.validate({\n  payment: {\n    amount: 1200,\n    currency: \"ARS\",\n  },\n  form: {\n    site: \"03101980\",\n    payment_method_id: 1,\n  },\n});\n\nconsole.log(validation);\n```\n\n### Consideraciones para checkout hospedado\n\n- `template_id: 1` crea un checkout estandar.\n- `template_id: 2` se usa para checkout con Cybersource cuando tu configuracion lo soporta.\n- `success_url` y `cancel_url` son las URLs de retorno del comprador.\n- `redirect_url` y `notifications_url` dependen del flujo configurado en tu cuenta de Payway.\n- El `payment_link` devuelto es el dato principal que debes almacenar o redirigir.\n\n## Scripts\n\n- `npm run typecheck`\n- `npm test`\n- `npm run build`\n- `npm run docs`\n\n## Guias\n\n- [docs/arquitectura.md](docs/arquitectura.md)\n- [docs/uso-basico.md](docs/uso-basico.md)\n- [docs/pagos-offline.md](docs/pagos-offline.md)\n\n## Alcance\n\nLa libreria expone una API orientada a backend para pagos, refunds, consulta de operaciones, tokenizacion, 3DS, checkout server-side, batch closure e internal tokenization. El foco es ofrecer una interfaz consistente para integraciones Node sobre Payway AR.\n","readmeFilename":"README.md"}