@crosspay-lb/js 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,17 +10,17 @@ npm install @crosspay-lb/js
10
10
  ```ts
11
11
  import { init } from '@crosspay-lb/js'
12
12
 
13
- const cp = init({ environment: 'production' })
14
- const fields = await cp.card.mount({ container: '#card', amount: 50000, currency: 'COP' })
15
- const result = await cp.payments.payWithCard(fields, {
16
- amount: 50000,
17
- currency: 'COP',
18
- description: 'Orden 1001',
19
- customer: { firstName: 'Ana', lastName: 'Díaz', email: 'ana@correo.com' },
20
- })
13
+ const cp = init({ environment: 'production', publishableKey: 'pk_live_…' })
14
+
15
+ // clientSecret: lo crea TU servidor con @crosspay-lb/node (sessions.create)
16
+ const fields = await cp.card.mount({ container: '#card', clientSecret })
17
+ const result = await fields.pay()
21
18
  ```
22
19
 
23
- O por CDN: `<script src="https://cdn.jsdelivr.net/npm/@crosspay-lb/js@0/dist/crosspay.min.js"></script>` → `window.Crosspay`.
20
+ O por CDN: `<script src="https://cdn.jsdelivr.net/npm/@crosspay-lb/js@0.2/dist/crosspay.min.js"></script>` → `window.Crosspay`.
21
+
22
+ Requisitos: llave pública del comercio, sitio en sus **dominios autorizados** y
23
+ una sesión de pago creada en el servidor con la llave secreta.
24
24
 
25
25
  Documentación completa: panel de Crosspay → **Documentación de la API** → *crosspay.js*.
26
26
 
@@ -1 +1 @@
1
- "use strict";var Crosspay=(()=>{var h=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var j=Object.prototype.hasOwnProperty;var z=(e,t)=>{for(var r in t)h(e,r,{get:t[r],enumerable:!0})},H=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of N(t))!j.call(e,n)&&n!==r&&h(e,n,{get:()=>t[n],enumerable:!(o=B(t,n))||o.enumerable});return e};var q=e=>H(h({},"__esModule",{value:!0}),e);var X={};z(X,{API_BASE_URLS:()=>g,CrosspayError:()=>a,init:()=>I,version:()=>A});var a=class extends Error{constructor(r,o,n){super(r);this.code=o;this.details=n;this.name="CrosspayError"}};var C="crosspay-fields/1";function x(e,t){return typeof e=="object"&&e!==null&&e.protocol===C&&e.channel===t&&typeof e.kind=="string"}function E(e){return new URLSearchParams({channel:e.channel,origin:e.parentOrigin}).toString()}var V=3e4,W={position:"fixed",inset:"0",width:"100%",height:"100%",zIndex:"2147483647"};function S(e,t){let r=typeof t.container=="string"?document.querySelector(t.container):t.container;if(!r)return Promise.reject(new a(`No existe el contenedor ${String(t.container)}.`,"INVALID_CONTAINER"));let o=new URL(e).origin,n=b(),d=document.createElement("div");d.style.position="relative";let s=document.createElement("iframe");s.src=`${e}#${E({channel:n,parentOrigin:window.location.origin})}`,s.title="Formulario seguro de tarjeta",s.setAttribute("scrolling","no"),s.setAttribute("allow","payment"),Object.assign(s.style,{display:"block",width:"100%",height:"0px",border:"0",background:"transparent",colorScheme:"normal"}),d.appendChild(s),r.replaceChildren(d);let p=0,i=new Map,u=l=>{s.contentWindow?.postMessage({...l,protocol:C,channel:n},o)},_=l=>{l?(d.style.height=`${p}px`,Object.assign(s.style,W)):(d.style.height="",Object.assign(s.style,{position:"",inset:"",width:"100%",height:`${p}px`,zIndex:""}))},f=!1,T=()=>{if(!f){f=!0,window.removeEventListener("message",v);for(let l of i.values())l.reject(new a("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));i.clear(),d.remove()}},y=null;function v(l){if(l.source!==s.contentWindow||l.origin!==o||!x(l.data,n))return;let c=l.data;switch(c.kind){case"ready":u({kind:"init",options:{currency:t.currency,amount:t.amount,isSubscription:t.isSubscription,installments:t.installments,labels:t.labels,styles:t.styles}});break;case"mounted":y?.();break;case"mount-error":y?.(new a(c.message,c.code));break;case"resize":p=Math.max(0,Math.ceil(c.height)),s.style.position!=="fixed"?s.style.height=`${p}px`:d.style.height=`${p}px`;break;case"validity":t.onValidityChange?.(c.valid);break;case"overlay":_(c.active);break;case"token":{let m=i.get(c.requestId);i.delete(c.requestId),m?.resolve(c.token);break}case"token-error":{let m=i.get(c.requestId);i.delete(c.requestId),m?.reject(new a(c.message,c.code));break}}}window.addEventListener("message",v);let $={tokenize(){if(f)return Promise.reject(new a("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));let l=b();return new Promise((c,m)=>{i.set(l,{resolve:c,reject:m}),u({kind:"tokenize",requestId:l})})},destroy:T};return new Promise((l,c)=>{let m=setTimeout(()=>{y?.(new a("No se pudo cargar el formulario de tarjeta.","FIELDS_TIMEOUT"))},V);y=w=>{y=null,clearTimeout(m),w?(T(),c(w)):l($)}})}function b(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`}var g={development:"https://api-dev.crosspaysolutions.financial",production:"https://api.crosspaysolutions.financial"};function O(e){let t=g[e];if(!t)throw new TypeError(`Ambiente de Crosspay no valido: "${String(e)}". Usa "development" o "production".`);return t}var k={development:"https://fields-dev.crosspaysolutions.financial/",production:"https://fields.crosspaysolutions.financial/"};function F(e){let t=(o,n)=>(e.fetch??globalThis.fetch)(o,n);async function r(o,n,d){let s={Accept:"application/json"};d!==void 0&&(s["Content-Type"]="application/json"),e.companyId&&(s["X-Company-Id"]=e.companyId),e.locale&&(s["Accept-Language"]=e.locale);let p;try{p=await t(`${e.baseUrl}${n}`,{method:o,headers:s,body:d===void 0?void 0:JSON.stringify(d),credentials:"omit"})}catch(u){throw new a("No se pudo conectar con Crosspay.","NETWORK_ERROR",u)}let i=await p.json().catch(()=>null);if(!p.ok||i?.success===!1){let u=i?.message||i?.error||`Crosspay respondio ${p.status}.`;throw new a(u,`HTTP_${p.status}`,i)}return i&&typeof i=="object"&&"data"in i?i.data:i}return{get:o=>r("GET",o),post:(o,n)=>r("POST",o,n)}}var R="cp_device_id";function Y(){try{let e=localStorage.getItem(R);return e||(e=typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`,localStorage.setItem(R,e)),e}catch{return""}}function G(){return typeof navigator>"u"?"":[navigator.userAgent,navigator.language,typeof screen<"u"?`${screen.width}x${screen.height}`:"",P()].join("|")}function K(e){let t=5381;for(let r=0;r<e.length;r+=1)t=(t<<5)+t+e.charCodeAt(r)|0;return(t>>>0).toString(16)}function P(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch{return""}}function U(){let e=Y(),t=K(G());return e?`${e}.${t}`:t}var J="crosspay.js";function D(e){let t=`${e.customer.firstName} ${e.customer.lastName}`.trim(),r=typeof navigator<"u"?navigator.language?.split("-")[0]:void 0;return{paymentMethod:e.method,amount:e.amount,currency:e.currency.toUpperCase(),description:e.description,reference:e.reference?.trim()||`REF-${e.method.toUpperCase()}-${Date.now()}`,customerEmail:e.customer.email,customerName:t,customer:e.customer,platform:J,..."cardToken"in e?{token:e.cardToken.token,...e.cardToken.deferred?{deferred:e.cardToken.deferred}:{}}:{},..."pse"in e?{pse:e.pse}:{},..."tokenizationDecline"in e?{tokenizationDecline:e.tokenizationDecline}:{},...e.returnUrl?{returnUrl:e.returnUrl}:{},meta:{...e.metadata??{},...r?{language:r}:{},fingerprint:U(),timezone:P()}}}function M(e){return{create(t){return e.post("/payments/process",D(t))},reportTokenizationDecline(t,r){return e.post("/payments/process",D({...t,tokenizationDecline:r}))},status(t){return e.get(`/payments/status/${encodeURIComponent(t)}`)}}}function L(e){return{async listBanks(t="COP"){return(await e.get(`/payments/pse/banks?currency=${encodeURIComponent(t)}`)??[]).filter(o=>{let n=String(o.code??"").trim(),d=String(o.name??"").normalize("NFD").replace(/\p{Diacritic}/gu,"").toLowerCase();return n!==""&&n!=="0"&&!d.includes("seleccione su banco")})}}}function I(e){let t=F({baseUrl:O(e.environment),companyId:e.companyId,locale:e.locale,fetch:e.fetch});if(e.fieldsUrl&&e.environment!=="development")throw new TypeError('`fieldsUrl` solo se admite en el ambiente "development".');let r=e.fieldsUrl??k[e.environment],o=M(t);return{environment:e.environment,card:{async mount(n){if(!n.isSubscription&&!(Number(n.amount)>0))throw new a("`amount` debe ser mayor a 0.","INVALID_AMOUNT");return S(r,n)}},payments:{...o,async payWithCard(n,d){let s;try{s=await n.tokenize()}catch(p){throw p instanceof a&&await o.reportTokenizationDecline({...d,method:"card"},{code:p.code,message:p.message}).catch(()=>{}),p}return o.create({...d,method:"card",cardToken:s})}},pse:L(t)}}var A="0.1.0";return q(X);})();
1
+ "use strict";var Crosspay=(()=>{var v=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var H=Object.prototype.hasOwnProperty;var z=(e,t)=>{for(var n in t)v(e,n,{get:t[n],enumerable:!0})},K=(e,t,n,p)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of N(t))!H.call(e,r)&&r!==n&&v(e,r,{get:()=>t[r],enumerable:!(p=q(t,r))||p.enumerable});return e};var Y=e=>K(v({},"__esModule",{value:!0}),e);var ee={};z(ee,{API_BASE_URLS:()=>f,CrosspayError:()=>a,init:()=>b,version:()=>$});var a=class extends Error{constructor(n,p,r){super(n);this.code=p;this.details=r;this.name="CrosspayError"}};var w="crosspay-fields/2";function I(e,t){return typeof e=="object"&&e!==null&&e.protocol===w&&e.channel===t&&typeof e.kind=="string"}function T(e){return new URLSearchParams({channel:e.channel,origin:e.parentOrigin}).toString()}var V=3e4,W={position:"fixed",inset:"0",width:"100%",height:"100%",zIndex:"2147483647"};function O(e,t,n){let p=typeof n.container=="string"?document.querySelector(n.container):n.container;if(!p)return Promise.reject(new a(`No existe el contenedor ${String(n.container)}.`,"INVALID_CONTAINER"));let r=new URL(e).origin,o=R(),i=document.createElement("div");i.style.position="relative";let s=document.createElement("iframe");s.src=`${e}#${T({channel:o,parentOrigin:window.location.origin})}`,s.title="Formulario seguro de tarjeta",s.setAttribute("scrolling","no"),s.setAttribute("allow","payment"),Object.assign(s.style,{display:"block",width:"100%",height:"0px",border:"0",background:"transparent",colorScheme:"normal"}),i.appendChild(s),p.replaceChildren(i);let l=0,d=new Map,u=m=>{s.contentWindow?.postMessage({...m,protocol:w,channel:o},r)},D=m=>{m?(i.style.height=`${l}px`,Object.assign(s.style,W)):(i.style.height="",Object.assign(s.style,{position:"",inset:"",width:"100%",height:`${l}px`,zIndex:""}))},h=!1,x=()=>{if(!h){h=!0,window.removeEventListener("message",k);for(let m of d.values())m.reject(new a("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));d.clear(),i.remove()}},y=null;function k(m){if(m.source!==s.contentWindow||m.origin!==r||!I(m.data,o))return;let c=m.data;switch(c.kind){case"ready":u({kind:"init",options:{publishableKey:t,clientSecret:n.clientSecret,installments:n.installments,labels:n.labels,styles:n.styles}});break;case"mounted":y?.(c.session);break;case"mount-error":y?.(new a(c.message,c.code));break;case"resize":l=Math.max(0,Math.ceil(c.height)),s.style.position!=="fixed"?s.style.height=`${l}px`:i.style.height=`${l}px`;break;case"validity":n.onValidityChange?.(c.valid);break;case"overlay":D(c.active);break;case"paid":{let g=d.get(c.requestId);d.delete(c.requestId),g?.resolve(c.result);break}case"pay-error":{let g=d.get(c.requestId);d.delete(c.requestId),g?.reject(new a(c.message,c.code));break}}}return window.addEventListener("message",k),new Promise((m,c)=>{let g=setTimeout(()=>{y?.(new a("No se pudo cargar el formulario de tarjeta.","FIELDS_TIMEOUT"))},V);y=S=>{if(y=null,clearTimeout(g),S instanceof a){x(),c(S);return}m({session:S,pay(){if(h)return Promise.reject(new a("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));let P=R();return new Promise((j,B)=>{d.set(P,{resolve:j,reject:B}),u({kind:"pay",requestId:P})})},destroy:x})}})}function R(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`}var f={development:"https://api-dev.crosspaysolutions.financial",production:"https://api.crosspaysolutions.financial"};function F(e){let t=f[e];if(!t)throw new TypeError(`Ambiente de Crosspay no valido: "${String(e)}". Usa "development" o "production".`);return t}var C={development:"https://fields-dev.crosspaysolutions.financial/",production:"https://fields.crosspaysolutions.financial/"};function U(e){let t=(r,o)=>(e.fetch??globalThis.fetch)(r,o);async function n(r,o,i){let s={Accept:"application/json"};i!==void 0&&(s["Content-Type"]="application/json"),e.locale&&(s["Accept-Language"]=e.locale);let l;try{l=await t(`${e.baseUrl}${o}`,{method:r,headers:s,body:i===void 0?void 0:JSON.stringify(i),credentials:"omit"})}catch(u){throw new a("No se pudo conectar con Crosspay.","NETWORK_ERROR",u)}let d=await l.json().catch(()=>null);if(!l.ok||d?.success===!1){let u=d?.message||d?.error||`Crosspay respondio ${l.status}.`;throw new a(u,`HTTP_${l.status}`,d)}return d}async function p(r,o,i){let s=await n(r,o,i);return s&&typeof s=="object"&&"data"in s?s.data:s}return{get:r=>p("GET",r),post:(r,o)=>p("POST",r,o),postRaw:(r,o)=>n("POST",r,o)}}var _="cp_device_id";function G(){try{let e=localStorage.getItem(_);return e||(e=typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`,localStorage.setItem(_,e)),e}catch{return""}}function J(){return typeof navigator>"u"?"":[navigator.userAgent,navigator.language,typeof screen<"u"?`${screen.width}x${screen.height}`:"",E()].join("|")}function Q(e){let t=5381;for(let n=0;n<e.length;n+=1)t=(t<<5)+t+e.charCodeAt(n)|0;return(t>>>0).toString(16)}function E(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch{return""}}function L(){let e=G(),t=Q(J());return e?`${e}.${t}`:t}function X(){let e=typeof navigator<"u"?navigator.language?.split("-")[0]:void 0;return{fingerprint:L(),timezone:E(),...e?{language:e}:{}}}function M(e){return{status(t){return e.get(`/payments/status/${encodeURIComponent(t)}`)}}}function A(e,t){return{async listBanks(n="COP"){return(await e.get(`/payments/pse/banks?currency=${encodeURIComponent(n)}`)??[]).filter(r=>{let o=String(r.code??"").trim(),i=String(r.name??"").normalize("NFD").replace(/\p{Diacritic}/gu,"").toLowerCase();return o!==""&&o!=="0"&&!i.includes("seleccione su banco")})},async pay(n){let{clientSecret:p,...r}=n,o=await e.postRaw("/payments/sessions/pay",{publishableKey:t,clientSecret:p,parentOrigin:window.location.origin,method:"pse",pse:r,meta:X()}),{data:i,...s}=o??{},l=i?.sessionId;return{...s,...l?{sessionId:l}:{}}}}}var Z={development:"pk_test_",production:"pk_live_"};function b(e){let t=F(e.environment),n=String(e.publishableKey??"").trim(),p=Z[e.environment];if(!n.startsWith(p))throw new TypeError(`\`publishableKey\` debe empezar por "${p}" en el ambiente "${e.environment}".`);if(e.fieldsUrl&&e.environment!=="development")throw new TypeError('`fieldsUrl` solo se admite en el ambiente "development".');let r=e.fieldsUrl??C[e.environment],o=U({baseUrl:t,locale:e.locale,fetch:e.fetch});return{environment:e.environment,card:{async mount(i){if(!String(i.clientSecret??"").trim())throw new a("Falta `clientSecret`: crea la sesion de pago en tu servidor con @crosspay-lb/node.","INVALID_REQUEST");return O(r,n,i)}},payments:M(o),pse:A(o,n)}}var $="0.2.0";return Y(ee);})();
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var h=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var j=(e,t)=>{for(var r in t)h(e,r,{get:t[r],enumerable:!0})},z=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let n of N(t))!B.call(e,n)&&n!==r&&h(e,n,{get:()=>t[n],enumerable:!(o=_(t,n))||o.enumerable});return e};var H=e=>z(h({},"__esModule",{value:!0}),e);var X={};j(X,{API_BASE_URLS:()=>k,CrosspayError:()=>c,FIELDS_URLS:()=>g,init:()=>L,version:()=>J});module.exports=H(X);var c=class extends Error{constructor(r,o,n){super(r);this.code=o;this.details=n;this.name="CrosspayError"}};var C="crosspay-fields/1";function v(e,t){return typeof e=="object"&&e!==null&&e.protocol===C&&e.channel===t&&typeof e.kind=="string"}function x(e){return new URLSearchParams({channel:e.channel,origin:e.parentOrigin}).toString()}var q=3e4,V={position:"fixed",inset:"0",width:"100%",height:"100%",zIndex:"2147483647"};function E(e,t){let r=typeof t.container=="string"?document.querySelector(t.container):t.container;if(!r)return Promise.reject(new c(`No existe el contenedor ${String(t.container)}.`,"INVALID_CONTAINER"));let o=new URL(e).origin,n=b(),d=document.createElement("div");d.style.position="relative";let s=document.createElement("iframe");s.src=`${e}#${x({channel:n,parentOrigin:window.location.origin})}`,s.title="Formulario seguro de tarjeta",s.setAttribute("scrolling","no"),s.setAttribute("allow","payment"),Object.assign(s.style,{display:"block",width:"100%",height:"0px",border:"0",background:"transparent",colorScheme:"normal"}),d.appendChild(s),r.replaceChildren(d);let p=0,a=new Map,u=l=>{s.contentWindow?.postMessage({...l,protocol:C,channel:n},o)},$=l=>{l?(d.style.height=`${p}px`,Object.assign(s.style,V)):(d.style.height="",Object.assign(s.style,{position:"",inset:"",width:"100%",height:`${p}px`,zIndex:""}))},f=!1,T=()=>{if(!f){f=!0,window.removeEventListener("message",I);for(let l of a.values())l.reject(new c("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));a.clear(),d.remove()}},y=null;function I(l){if(l.source!==s.contentWindow||l.origin!==o||!v(l.data,n))return;let i=l.data;switch(i.kind){case"ready":u({kind:"init",options:{currency:t.currency,amount:t.amount,isSubscription:t.isSubscription,installments:t.installments,labels:t.labels,styles:t.styles}});break;case"mounted":y?.();break;case"mount-error":y?.(new c(i.message,i.code));break;case"resize":p=Math.max(0,Math.ceil(i.height)),s.style.position!=="fixed"?s.style.height=`${p}px`:d.style.height=`${p}px`;break;case"validity":t.onValidityChange?.(i.valid);break;case"overlay":$(i.active);break;case"token":{let m=a.get(i.requestId);a.delete(i.requestId),m?.resolve(i.token);break}case"token-error":{let m=a.get(i.requestId);a.delete(i.requestId),m?.reject(new c(i.message,i.code));break}}}window.addEventListener("message",I);let A={tokenize(){if(f)return Promise.reject(new c("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));let l=b();return new Promise((i,m)=>{a.set(l,{resolve:i,reject:m}),u({kind:"tokenize",requestId:l})})},destroy:T};return new Promise((l,i)=>{let m=setTimeout(()=>{y?.(new c("No se pudo cargar el formulario de tarjeta.","FIELDS_TIMEOUT"))},q);y=w=>{y=null,clearTimeout(m),w?(T(),i(w)):l(A)}})}function b(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`}var k={development:"https://api-dev.crosspaysolutions.financial",production:"https://api.crosspaysolutions.financial"};function S(e){let t=k[e];if(!t)throw new TypeError(`Ambiente de Crosspay no valido: "${String(e)}". Usa "development" o "production".`);return t}var g={development:"https://fields-dev.crosspaysolutions.financial/",production:"https://fields.crosspaysolutions.financial/"};function O(e){let t=(o,n)=>(e.fetch??globalThis.fetch)(o,n);async function r(o,n,d){let s={Accept:"application/json"};d!==void 0&&(s["Content-Type"]="application/json"),e.companyId&&(s["X-Company-Id"]=e.companyId),e.locale&&(s["Accept-Language"]=e.locale);let p;try{p=await t(`${e.baseUrl}${n}`,{method:o,headers:s,body:d===void 0?void 0:JSON.stringify(d),credentials:"omit"})}catch(u){throw new c("No se pudo conectar con Crosspay.","NETWORK_ERROR",u)}let a=await p.json().catch(()=>null);if(!p.ok||a?.success===!1){let u=a?.message||a?.error||`Crosspay respondio ${p.status}.`;throw new c(u,`HTTP_${p.status}`,a)}return a&&typeof a=="object"&&"data"in a?a.data:a}return{get:o=>r("GET",o),post:(o,n)=>r("POST",o,n)}}var F="cp_device_id";function W(){try{let e=localStorage.getItem(F);return e||(e=typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`,localStorage.setItem(F,e)),e}catch{return""}}function Y(){return typeof navigator>"u"?"":[navigator.userAgent,navigator.language,typeof screen<"u"?`${screen.width}x${screen.height}`:"",P()].join("|")}function G(e){let t=5381;for(let r=0;r<e.length;r+=1)t=(t<<5)+t+e.charCodeAt(r)|0;return(t>>>0).toString(16)}function P(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch{return""}}function R(){let e=W(),t=G(Y());return e?`${e}.${t}`:t}var K="crosspay.js";function U(e){let t=`${e.customer.firstName} ${e.customer.lastName}`.trim(),r=typeof navigator<"u"?navigator.language?.split("-")[0]:void 0;return{paymentMethod:e.method,amount:e.amount,currency:e.currency.toUpperCase(),description:e.description,reference:e.reference?.trim()||`REF-${e.method.toUpperCase()}-${Date.now()}`,customerEmail:e.customer.email,customerName:t,customer:e.customer,platform:K,..."cardToken"in e?{token:e.cardToken.token,...e.cardToken.deferred?{deferred:e.cardToken.deferred}:{}}:{},..."pse"in e?{pse:e.pse}:{},..."tokenizationDecline"in e?{tokenizationDecline:e.tokenizationDecline}:{},...e.returnUrl?{returnUrl:e.returnUrl}:{},meta:{...e.metadata??{},...r?{language:r}:{},fingerprint:R(),timezone:P()}}}function D(e){return{create(t){return e.post("/payments/process",U(t))},reportTokenizationDecline(t,r){return e.post("/payments/process",U({...t,tokenizationDecline:r}))},status(t){return e.get(`/payments/status/${encodeURIComponent(t)}`)}}}function M(e){return{async listBanks(t="COP"){return(await e.get(`/payments/pse/banks?currency=${encodeURIComponent(t)}`)??[]).filter(o=>{let n=String(o.code??"").trim(),d=String(o.name??"").normalize("NFD").replace(/\p{Diacritic}/gu,"").toLowerCase();return n!==""&&n!=="0"&&!d.includes("seleccione su banco")})}}}function L(e){let t=O({baseUrl:S(e.environment),companyId:e.companyId,locale:e.locale,fetch:e.fetch});if(e.fieldsUrl&&e.environment!=="development")throw new TypeError('`fieldsUrl` solo se admite en el ambiente "development".');let r=e.fieldsUrl??g[e.environment],o=D(t);return{environment:e.environment,card:{async mount(n){if(!n.isSubscription&&!(Number(n.amount)>0))throw new c("`amount` debe ser mayor a 0.","INVALID_AMOUNT");return E(r,n)}},payments:{...o,async payWithCard(n,d){let s;try{s=await n.tokenize()}catch(p){throw p instanceof c&&await o.reportTokenizationDecline({...d,method:"card"},{code:p.code,message:p.message}).catch(()=>{}),p}return o.create({...d,method:"card",cardToken:s})}},pse:M(t)}}var J="0.1.0";0&&(module.exports={API_BASE_URLS,CrosspayError,FIELDS_URLS,init,version});
1
+ "use strict";var S=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var H=(e,t)=>{for(var n in t)S(e,n,{get:t[n],enumerable:!0})},z=(e,t,n,p)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of q(t))!N.call(e,r)&&r!==n&&S(e,r,{get:()=>t[r],enumerable:!(p=B(t,r))||p.enumerable});return e};var K=e=>z(S({},"__esModule",{value:!0}),e);var ee={};H(ee,{API_BASE_URLS:()=>C,CrosspayError:()=>a,FIELDS_URLS:()=>f,init:()=>$,version:()=>Z});module.exports=K(ee);var a=class extends Error{constructor(n,p,r){super(n);this.code=p;this.details=r;this.name="CrosspayError"}};var w="crosspay-fields/2";function P(e,t){return typeof e=="object"&&e!==null&&e.protocol===w&&e.channel===t&&typeof e.kind=="string"}function T(e){return new URLSearchParams({channel:e.channel,origin:e.parentOrigin}).toString()}var Y=3e4,V={position:"fixed",inset:"0",width:"100%",height:"100%",zIndex:"2147483647"};function R(e,t,n){let p=typeof n.container=="string"?document.querySelector(n.container):n.container;if(!p)return Promise.reject(new a(`No existe el contenedor ${String(n.container)}.`,"INVALID_CONTAINER"));let r=new URL(e).origin,o=I(),i=document.createElement("div");i.style.position="relative";let s=document.createElement("iframe");s.src=`${e}#${T({channel:o,parentOrigin:window.location.origin})}`,s.title="Formulario seguro de tarjeta",s.setAttribute("scrolling","no"),s.setAttribute("allow","payment"),Object.assign(s.style,{display:"block",width:"100%",height:"0px",border:"0",background:"transparent",colorScheme:"normal"}),i.appendChild(s),p.replaceChildren(i);let l=0,d=new Map,u=m=>{s.contentWindow?.postMessage({...m,protocol:w,channel:o},r)},A=m=>{m?(i.style.height=`${l}px`,Object.assign(s.style,V)):(i.style.height="",Object.assign(s.style,{position:"",inset:"",width:"100%",height:`${l}px`,zIndex:""}))},h=!1,b=()=>{if(!h){h=!0,window.removeEventListener("message",x);for(let m of d.values())m.reject(new a("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));d.clear(),i.remove()}},y=null;function x(m){if(m.source!==s.contentWindow||m.origin!==r||!P(m.data,o))return;let c=m.data;switch(c.kind){case"ready":u({kind:"init",options:{publishableKey:t,clientSecret:n.clientSecret,installments:n.installments,labels:n.labels,styles:n.styles}});break;case"mounted":y?.(c.session);break;case"mount-error":y?.(new a(c.message,c.code));break;case"resize":l=Math.max(0,Math.ceil(c.height)),s.style.position!=="fixed"?s.style.height=`${l}px`:i.style.height=`${l}px`;break;case"validity":n.onValidityChange?.(c.valid);break;case"overlay":A(c.active);break;case"paid":{let g=d.get(c.requestId);d.delete(c.requestId),g?.resolve(c.result);break}case"pay-error":{let g=d.get(c.requestId);d.delete(c.requestId),g?.reject(new a(c.message,c.code));break}}}return window.addEventListener("message",x),new Promise((m,c)=>{let g=setTimeout(()=>{y?.(new a("No se pudo cargar el formulario de tarjeta.","FIELDS_TIMEOUT"))},Y);y=v=>{if(y=null,clearTimeout(g),v instanceof a){b(),c(v);return}m({session:v,pay(){if(h)return Promise.reject(new a("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));let k=I();return new Promise((D,j)=>{d.set(k,{resolve:D,reject:j}),u({kind:"pay",requestId:k})})},destroy:b})}})}function I(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`}var C={development:"https://api-dev.crosspaysolutions.financial",production:"https://api.crosspaysolutions.financial"};function O(e){let t=C[e];if(!t)throw new TypeError(`Ambiente de Crosspay no valido: "${String(e)}". Usa "development" o "production".`);return t}var f={development:"https://fields-dev.crosspaysolutions.financial/",production:"https://fields.crosspaysolutions.financial/"};function F(e){let t=(r,o)=>(e.fetch??globalThis.fetch)(r,o);async function n(r,o,i){let s={Accept:"application/json"};i!==void 0&&(s["Content-Type"]="application/json"),e.locale&&(s["Accept-Language"]=e.locale);let l;try{l=await t(`${e.baseUrl}${o}`,{method:r,headers:s,body:i===void 0?void 0:JSON.stringify(i),credentials:"omit"})}catch(u){throw new a("No se pudo conectar con Crosspay.","NETWORK_ERROR",u)}let d=await l.json().catch(()=>null);if(!l.ok||d?.success===!1){let u=d?.message||d?.error||`Crosspay respondio ${l.status}.`;throw new a(u,`HTTP_${l.status}`,d)}return d}async function p(r,o,i){let s=await n(r,o,i);return s&&typeof s=="object"&&"data"in s?s.data:s}return{get:r=>p("GET",r),post:(r,o)=>p("POST",r,o),postRaw:(r,o)=>n("POST",r,o)}}var U="cp_device_id";function W(){try{let e=localStorage.getItem(U);return e||(e=typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`,localStorage.setItem(U,e)),e}catch{return""}}function G(){return typeof navigator>"u"?"":[navigator.userAgent,navigator.language,typeof screen<"u"?`${screen.width}x${screen.height}`:"",E()].join("|")}function J(e){let t=5381;for(let n=0;n<e.length;n+=1)t=(t<<5)+t+e.charCodeAt(n)|0;return(t>>>0).toString(16)}function E(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch{return""}}function L(){let e=W(),t=J(G());return e?`${e}.${t}`:t}function Q(){let e=typeof navigator<"u"?navigator.language?.split("-")[0]:void 0;return{fingerprint:L(),timezone:E(),...e?{language:e}:{}}}function M(e){return{status(t){return e.get(`/payments/status/${encodeURIComponent(t)}`)}}}function _(e,t){return{async listBanks(n="COP"){return(await e.get(`/payments/pse/banks?currency=${encodeURIComponent(n)}`)??[]).filter(r=>{let o=String(r.code??"").trim(),i=String(r.name??"").normalize("NFD").replace(/\p{Diacritic}/gu,"").toLowerCase();return o!==""&&o!=="0"&&!i.includes("seleccione su banco")})},async pay(n){let{clientSecret:p,...r}=n,o=await e.postRaw("/payments/sessions/pay",{publishableKey:t,clientSecret:p,parentOrigin:window.location.origin,method:"pse",pse:r,meta:Q()}),{data:i,...s}=o??{},l=i?.sessionId;return{...s,...l?{sessionId:l}:{}}}}}var X={development:"pk_test_",production:"pk_live_"};function $(e){let t=O(e.environment),n=String(e.publishableKey??"").trim(),p=X[e.environment];if(!n.startsWith(p))throw new TypeError(`\`publishableKey\` debe empezar por "${p}" en el ambiente "${e.environment}".`);if(e.fieldsUrl&&e.environment!=="development")throw new TypeError('`fieldsUrl` solo se admite en el ambiente "development".');let r=e.fieldsUrl??f[e.environment],o=F({baseUrl:t,locale:e.locale,fetch:e.fetch});return{environment:e.environment,card:{async mount(i){if(!String(i.clientSecret??"").trim())throw new a("Falta `clientSecret`: crea la sesion de pago en tu servidor con @crosspay-lb/node.","INVALID_REQUEST");return R(r,n,i)}},payments:M(o),pse:_(o,n)}}var Z="0.2.0";0&&(module.exports={API_BASE_URLS,CrosspayError,FIELDS_URLS,init,version});
package/dist/index.d.cts CHANGED
@@ -1,3 +1,84 @@
1
+ interface HttpClientOptions {
2
+ baseUrl: string;
3
+ locale?: string;
4
+ fetch?: typeof fetch;
5
+ }
6
+ /**
7
+ * Cliente HTTP minimo sobre `fetch`.
8
+ *
9
+ * Desde el navegador NO se manda la llave secreta: el backend reconoce al
10
+ * comercio por su llave publica, el `Origin` de su dominio autorizado y la
11
+ * sesion que creo su servidor. La llave secreta solo va en `@crosspay-lb/node`.
12
+ *
13
+ * El backend responde `{ success, data }`; aqui se desenvuelve `data` y un
14
+ * `success: false` se convierte en `CrosspayError`.
15
+ */
16
+ declare function createHttpClient(options: HttpClientOptions): {
17
+ get: <T>(path: string) => Promise<T>;
18
+ post: <T>(path: string, body: unknown) => Promise<T>;
19
+ /** Sin desenvolver: para respuestas planas como la del cobro. */
20
+ postRaw: (path: string, body: unknown) => Promise<{
21
+ success?: boolean;
22
+ data?: unknown;
23
+ message?: string;
24
+ error?: string;
25
+ } | null>;
26
+ };
27
+ type HttpClient = ReturnType<typeof createHttpClient>;
28
+
29
+ type PaymentStatus = 'approved' | 'pending' | 'declined' | 'failed' | 'cancelled' | 'refunded' | (string & {});
30
+ interface PaymentResult {
31
+ success: boolean;
32
+ status: PaymentStatus;
33
+ message?: string;
34
+ transactionId: string | null;
35
+ sessionId?: string;
36
+ /** PSE: URL del banco a la que hay que llevar al pagador. */
37
+ redirectUrl?: string;
38
+ declineReason?: string | null;
39
+ amount?: number;
40
+ currency?: string;
41
+ reference?: string;
42
+ [key: string]: unknown;
43
+ }
44
+ interface Bank {
45
+ code: string;
46
+ name: string;
47
+ }
48
+ interface PsePayInput {
49
+ /** `clientSecret` de la sesion creada por el servidor del comercio. */
50
+ clientSecret: string;
51
+ /** Codigo del banco, de `pse.listBanks()`. */
52
+ bank: string;
53
+ documentType: string;
54
+ documentNumber: string;
55
+ /** `natural` o `juridica`. */
56
+ personType: string;
57
+ /** Direccion del pagador. Obligatoria para PSE. */
58
+ address?: string;
59
+ }
60
+ declare function createPaymentsApi(http: HttpClient): {
61
+ /** Estado publico de una transaccion (el mismo recurso del comprobante). */
62
+ status(transactionId: string): Promise<PaymentResult>;
63
+ };
64
+ declare function createPseApi(http: HttpClient, publishableKey: string): {
65
+ /** Bancos PSE (Colombia). Se quita la entrada guia "seleccione su banco". */
66
+ listBanks(currency?: string): Promise<Bank[]>;
67
+ /**
68
+ * Inicia el pago PSE de la sesion. Devuelve `redirectUrl`: hay que llevar
69
+ * al pagador al banco. Monto y cliente salen de la sesion.
70
+ */
71
+ pay(input: PsePayInput): Promise<PaymentResult>;
72
+ };
73
+
74
+ /** Lo que la pagina de campos confirma de la sesion al montar. */
75
+ interface SessionSummary {
76
+ sessionId: string;
77
+ amount: number;
78
+ currency: string;
79
+ description: string;
80
+ }
81
+
1
82
  interface CardFieldLabels {
2
83
  cardholderName?: string;
3
84
  cardNumber?: string;
@@ -8,12 +89,12 @@ interface CardFieldLabels {
8
89
  interface CardFieldsOptions {
9
90
  /** Donde se inserta el formulario: un selector CSS o el elemento. */
10
91
  container: string | HTMLElement;
11
- /** Moneda del cobro (ISO 4217). */
12
- currency: string;
13
- /** Monto total. Obligatorio salvo en suscripciones. */
14
- amount?: number;
15
- /** Tokeniza para una suscripcion (cobro recurrente) en vez de un cobro unico. */
16
- isSubscription?: boolean;
92
+ /**
93
+ * `clientSecret` de la sesion de pago que creo el SERVIDOR del comercio con
94
+ * su llave secreta (`sessions.create` de @crosspay-lb/node). Monto, moneda y
95
+ * cliente salen de la sesion: el navegador no puede cambiarlos.
96
+ */
97
+ clientSecret: string;
17
98
  /** Muestra el selector de cuotas. */
18
99
  installments?: boolean;
19
100
  labels?: CardFieldLabels;
@@ -26,25 +107,15 @@ interface CardFieldsOptions {
26
107
  /** Se dispara cada vez que cambia la validez del formulario. */
27
108
  onValidityChange?: (isValid: boolean) => void;
28
109
  }
29
- /** Token de un solo uso. Nunca contiene el numero de tarjeta. */
30
- interface CardToken {
31
- token: string;
32
- deferred?: {
33
- months?: number;
34
- creditType?: string;
35
- graceMonths?: number;
36
- };
37
- bin?: string;
38
- lastFourDigits?: string;
39
- brand?: string;
40
- }
41
110
  interface MountedCardFields {
111
+ /** Monto, moneda y descripcion de la sesion que se va a pagar. */
112
+ readonly session: SessionSummary;
42
113
  /**
43
- * Pide el token. Puede abrir la verificacion 3DS del banco a pantalla
44
- * completa, asi que puede tardar. Lanza `CrosspayError` si la tarjeta se
45
- * rechaza.
114
+ * Tokeniza y cobra la sesion. Puede abrir la verificacion 3DS del banco a
115
+ * pantalla completa, asi que puede tardar. Lanza `CrosspayError` si la
116
+ * tarjeta se rechaza o la sesion ya no se puede pagar.
46
117
  */
47
- tokenize(): Promise<CardToken>;
118
+ pay(): Promise<PaymentResult>;
48
119
  /** Retira el formulario. */
49
120
  destroy(): void;
50
121
  }
@@ -66,115 +137,16 @@ declare const API_BASE_URLS: Record<CrosspayEnvironment, string>;
66
137
  */
67
138
  declare const FIELDS_URLS: Record<CrosspayEnvironment, string>;
68
139
 
69
- interface HttpClientOptions {
70
- baseUrl: string;
71
- /** Id publico de la empresa (20 digitos). Viaja como `X-Company-Id`. */
72
- companyId?: string;
73
- locale?: string;
74
- fetch?: typeof fetch;
75
- }
76
- /**
77
- * Cliente HTTP minimo sobre `fetch`.
78
- *
79
- * Desde el navegador NO se manda API key: el backend autentica por el `Origin`
80
- * del sitio del comercio, que debe estar registrado en el CORS de su empresa.
81
- * La API key de integracion es secreta y solo va en `@crosspay-lb/node`.
82
- *
83
- * El backend responde `{ success, data }`; aqui se desenvuelve `data` y un
84
- * `success: false` se convierte en `CrosspayError`.
85
- */
86
- declare function createHttpClient(options: HttpClientOptions): {
87
- get: <T>(path: string) => Promise<T>;
88
- post: <T>(path: string, body: unknown) => Promise<T>;
89
- };
90
- type HttpClient = ReturnType<typeof createHttpClient>;
91
-
92
- interface Customer {
93
- firstName: string;
94
- lastName: string;
95
- email: string;
96
- phone?: string;
97
- documentType?: string;
98
- documentNumber?: string;
99
- }
100
- interface PseData {
101
- /** Codigo del banco, de `pse.listBanks()`. */
102
- bank: string;
103
- documentType: string;
104
- documentNumber: string;
105
- /** `natural` o `juridica`. */
106
- personType: string;
107
- /** Direccion del pagador. Obligatoria para PSE. */
108
- address?: string;
109
- }
110
- interface BasePaymentInput {
111
- amount: number;
112
- currency: string;
113
- description: string;
114
- /** Referencia del comercio (numero de orden, factura…). */
115
- reference?: string;
116
- customer: Customer;
117
- /** A donde vuelve el pagador cuando el medio lo saca de la pagina (PSE, 3DS). */
118
- returnUrl?: string;
119
- /** Datos libres del comercio. Viajan en `meta`. */
120
- metadata?: Record<string, unknown>;
121
- }
122
- interface CardPaymentInput extends BasePaymentInput {
123
- method: 'card';
124
- cardToken: CardToken;
125
- }
126
- interface PsePaymentInput extends BasePaymentInput {
127
- method: 'pse';
128
- pse: PseData;
129
- }
130
- type PaymentInput = CardPaymentInput | PsePaymentInput;
131
- type PaymentStatus = 'approved' | 'pending' | 'declined' | 'failed' | 'cancelled' | 'refunded' | (string & {});
132
- interface PaymentResult {
133
- success: boolean;
134
- status: PaymentStatus;
135
- message?: string;
136
- transactionId: string | null;
137
- gatewayTransactionId?: string | null;
138
- /** PSE: URL del banco a la que hay que llevar al pagador. */
139
- redirectUrl?: string;
140
- declineReason?: string | null;
141
- amount?: number;
142
- currency?: string;
143
- reference?: string;
144
- [key: string]: unknown;
145
- }
146
- interface Bank {
147
- code: string;
148
- name: string;
149
- }
150
- declare function createPaymentsApi(http: HttpClient): {
151
- /** Crea el cobro. Para tarjeta, el `cardToken` sale de `card.tokenize()`. */
152
- create(input: PaymentInput): Promise<PaymentResult>;
153
- /**
154
- * Registra un rechazo en la tokenizacion como transaccion declinada, para
155
- * trazabilidad. No cobra nada ni lleva datos de tarjeta.
156
- */
157
- reportTokenizationDecline(input: Omit<CardPaymentInput, "cardToken">, decline: {
158
- code?: string;
159
- message?: string;
160
- }): Promise<PaymentResult>;
161
- /** Estado publico de una transaccion (el mismo recurso del comprobante). */
162
- status(transactionId: string): Promise<PaymentResult>;
163
- };
164
- declare function createPseApi(http: HttpClient): {
165
- /** Bancos PSE (Colombia). Se quita la entrada guia "seleccione su banco". */
166
- listBanks(currency?: string): Promise<Bank[]>;
167
- };
168
-
169
140
  interface CrosspayOptions {
170
141
  /** `development` → api-dev.crosspaysolutions.financial · `production` → api.crosspaysolutions.financial */
171
142
  environment: CrosspayEnvironment;
172
143
  /**
173
- * Id publico de la empresa (20 digitos). Opcional: si el dominio del
174
- * comercio esta registrado en el CORS de su empresa, el backend la infiere
175
- * del `Origin`.
144
+ * Llave publica del comercio (panel → Integracion). `pk_test_…` en
145
+ * development, `pk_live_…` en production. Es publica por diseno: solo
146
+ * funciona desde los dominios autorizados del comercio y con una sesion
147
+ * creada por su servidor.
176
148
  */
177
- companyId?: string;
149
+ publishableKey: string;
178
150
  /** Idioma de los correos al pagador (`es`, `en`…). */
179
151
  locale?: string;
180
152
  /** Para pruebas. Por defecto, el `fetch` del navegador. */
@@ -190,18 +162,13 @@ interface Crosspay {
190
162
  readonly environment: CrosspayEnvironment;
191
163
  card: {
192
164
  /**
193
- * Monta el formulario seguro de tarjeta en `options.container`. Rechaza
194
- * con `CARD_UNAVAILABLE` si la moneda no admite tarjeta.
165
+ * Monta el formulario seguro de tarjeta en `options.container` para la
166
+ * sesion `options.clientSecret`. Rechaza con `CrosspayError` si la llave,
167
+ * la sesion o el dominio no son validos.
195
168
  */
196
169
  mount(options: CardFieldsOptions): Promise<MountedCardFields>;
197
170
  };
198
- payments: ReturnType<typeof createPaymentsApi> & {
199
- /**
200
- * Atajo: tokeniza y cobra. Si la tarjeta se rechaza al tokenizar, deja
201
- * registrada la transaccion declinada y relanza el `CrosspayError`.
202
- */
203
- payWithCard(fields: MountedCardFields, input: Omit<CardPaymentInput, 'cardToken' | 'method'>): Promise<PaymentResult>;
204
- };
171
+ payments: ReturnType<typeof createPaymentsApi>;
205
172
  pse: ReturnType<typeof createPseApi>;
206
173
  }
207
174
  declare function init(options: CrosspayOptions): Crosspay;
@@ -222,4 +189,4 @@ declare class CrosspayError extends Error {
222
189
  /** Version de la libreria, inyectada en el build. */
223
190
  declare const version: string;
224
191
 
225
- export { API_BASE_URLS, type Bank, type CardFieldLabels, type CardFieldsOptions, type CardPaymentInput, type CardToken, type Crosspay, type CrosspayEnvironment, CrosspayError, type CrosspayOptions, type Customer, FIELDS_URLS, type MountedCardFields, type PaymentInput, type PaymentResult, type PaymentStatus, type PseData, type PsePaymentInput, init, version };
192
+ export { API_BASE_URLS, type Bank, type CardFieldLabels, type CardFieldsOptions, type Crosspay, type CrosspayEnvironment, CrosspayError, type CrosspayOptions, FIELDS_URLS, type MountedCardFields, type PaymentResult, type PaymentStatus, type PsePayInput, type SessionSummary, init, version };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,84 @@
1
+ interface HttpClientOptions {
2
+ baseUrl: string;
3
+ locale?: string;
4
+ fetch?: typeof fetch;
5
+ }
6
+ /**
7
+ * Cliente HTTP minimo sobre `fetch`.
8
+ *
9
+ * Desde el navegador NO se manda la llave secreta: el backend reconoce al
10
+ * comercio por su llave publica, el `Origin` de su dominio autorizado y la
11
+ * sesion que creo su servidor. La llave secreta solo va en `@crosspay-lb/node`.
12
+ *
13
+ * El backend responde `{ success, data }`; aqui se desenvuelve `data` y un
14
+ * `success: false` se convierte en `CrosspayError`.
15
+ */
16
+ declare function createHttpClient(options: HttpClientOptions): {
17
+ get: <T>(path: string) => Promise<T>;
18
+ post: <T>(path: string, body: unknown) => Promise<T>;
19
+ /** Sin desenvolver: para respuestas planas como la del cobro. */
20
+ postRaw: (path: string, body: unknown) => Promise<{
21
+ success?: boolean;
22
+ data?: unknown;
23
+ message?: string;
24
+ error?: string;
25
+ } | null>;
26
+ };
27
+ type HttpClient = ReturnType<typeof createHttpClient>;
28
+
29
+ type PaymentStatus = 'approved' | 'pending' | 'declined' | 'failed' | 'cancelled' | 'refunded' | (string & {});
30
+ interface PaymentResult {
31
+ success: boolean;
32
+ status: PaymentStatus;
33
+ message?: string;
34
+ transactionId: string | null;
35
+ sessionId?: string;
36
+ /** PSE: URL del banco a la que hay que llevar al pagador. */
37
+ redirectUrl?: string;
38
+ declineReason?: string | null;
39
+ amount?: number;
40
+ currency?: string;
41
+ reference?: string;
42
+ [key: string]: unknown;
43
+ }
44
+ interface Bank {
45
+ code: string;
46
+ name: string;
47
+ }
48
+ interface PsePayInput {
49
+ /** `clientSecret` de la sesion creada por el servidor del comercio. */
50
+ clientSecret: string;
51
+ /** Codigo del banco, de `pse.listBanks()`. */
52
+ bank: string;
53
+ documentType: string;
54
+ documentNumber: string;
55
+ /** `natural` o `juridica`. */
56
+ personType: string;
57
+ /** Direccion del pagador. Obligatoria para PSE. */
58
+ address?: string;
59
+ }
60
+ declare function createPaymentsApi(http: HttpClient): {
61
+ /** Estado publico de una transaccion (el mismo recurso del comprobante). */
62
+ status(transactionId: string): Promise<PaymentResult>;
63
+ };
64
+ declare function createPseApi(http: HttpClient, publishableKey: string): {
65
+ /** Bancos PSE (Colombia). Se quita la entrada guia "seleccione su banco". */
66
+ listBanks(currency?: string): Promise<Bank[]>;
67
+ /**
68
+ * Inicia el pago PSE de la sesion. Devuelve `redirectUrl`: hay que llevar
69
+ * al pagador al banco. Monto y cliente salen de la sesion.
70
+ */
71
+ pay(input: PsePayInput): Promise<PaymentResult>;
72
+ };
73
+
74
+ /** Lo que la pagina de campos confirma de la sesion al montar. */
75
+ interface SessionSummary {
76
+ sessionId: string;
77
+ amount: number;
78
+ currency: string;
79
+ description: string;
80
+ }
81
+
1
82
  interface CardFieldLabels {
2
83
  cardholderName?: string;
3
84
  cardNumber?: string;
@@ -8,12 +89,12 @@ interface CardFieldLabels {
8
89
  interface CardFieldsOptions {
9
90
  /** Donde se inserta el formulario: un selector CSS o el elemento. */
10
91
  container: string | HTMLElement;
11
- /** Moneda del cobro (ISO 4217). */
12
- currency: string;
13
- /** Monto total. Obligatorio salvo en suscripciones. */
14
- amount?: number;
15
- /** Tokeniza para una suscripcion (cobro recurrente) en vez de un cobro unico. */
16
- isSubscription?: boolean;
92
+ /**
93
+ * `clientSecret` de la sesion de pago que creo el SERVIDOR del comercio con
94
+ * su llave secreta (`sessions.create` de @crosspay-lb/node). Monto, moneda y
95
+ * cliente salen de la sesion: el navegador no puede cambiarlos.
96
+ */
97
+ clientSecret: string;
17
98
  /** Muestra el selector de cuotas. */
18
99
  installments?: boolean;
19
100
  labels?: CardFieldLabels;
@@ -26,25 +107,15 @@ interface CardFieldsOptions {
26
107
  /** Se dispara cada vez que cambia la validez del formulario. */
27
108
  onValidityChange?: (isValid: boolean) => void;
28
109
  }
29
- /** Token de un solo uso. Nunca contiene el numero de tarjeta. */
30
- interface CardToken {
31
- token: string;
32
- deferred?: {
33
- months?: number;
34
- creditType?: string;
35
- graceMonths?: number;
36
- };
37
- bin?: string;
38
- lastFourDigits?: string;
39
- brand?: string;
40
- }
41
110
  interface MountedCardFields {
111
+ /** Monto, moneda y descripcion de la sesion que se va a pagar. */
112
+ readonly session: SessionSummary;
42
113
  /**
43
- * Pide el token. Puede abrir la verificacion 3DS del banco a pantalla
44
- * completa, asi que puede tardar. Lanza `CrosspayError` si la tarjeta se
45
- * rechaza.
114
+ * Tokeniza y cobra la sesion. Puede abrir la verificacion 3DS del banco a
115
+ * pantalla completa, asi que puede tardar. Lanza `CrosspayError` si la
116
+ * tarjeta se rechaza o la sesion ya no se puede pagar.
46
117
  */
47
- tokenize(): Promise<CardToken>;
118
+ pay(): Promise<PaymentResult>;
48
119
  /** Retira el formulario. */
49
120
  destroy(): void;
50
121
  }
@@ -66,115 +137,16 @@ declare const API_BASE_URLS: Record<CrosspayEnvironment, string>;
66
137
  */
67
138
  declare const FIELDS_URLS: Record<CrosspayEnvironment, string>;
68
139
 
69
- interface HttpClientOptions {
70
- baseUrl: string;
71
- /** Id publico de la empresa (20 digitos). Viaja como `X-Company-Id`. */
72
- companyId?: string;
73
- locale?: string;
74
- fetch?: typeof fetch;
75
- }
76
- /**
77
- * Cliente HTTP minimo sobre `fetch`.
78
- *
79
- * Desde el navegador NO se manda API key: el backend autentica por el `Origin`
80
- * del sitio del comercio, que debe estar registrado en el CORS de su empresa.
81
- * La API key de integracion es secreta y solo va en `@crosspay-lb/node`.
82
- *
83
- * El backend responde `{ success, data }`; aqui se desenvuelve `data` y un
84
- * `success: false` se convierte en `CrosspayError`.
85
- */
86
- declare function createHttpClient(options: HttpClientOptions): {
87
- get: <T>(path: string) => Promise<T>;
88
- post: <T>(path: string, body: unknown) => Promise<T>;
89
- };
90
- type HttpClient = ReturnType<typeof createHttpClient>;
91
-
92
- interface Customer {
93
- firstName: string;
94
- lastName: string;
95
- email: string;
96
- phone?: string;
97
- documentType?: string;
98
- documentNumber?: string;
99
- }
100
- interface PseData {
101
- /** Codigo del banco, de `pse.listBanks()`. */
102
- bank: string;
103
- documentType: string;
104
- documentNumber: string;
105
- /** `natural` o `juridica`. */
106
- personType: string;
107
- /** Direccion del pagador. Obligatoria para PSE. */
108
- address?: string;
109
- }
110
- interface BasePaymentInput {
111
- amount: number;
112
- currency: string;
113
- description: string;
114
- /** Referencia del comercio (numero de orden, factura…). */
115
- reference?: string;
116
- customer: Customer;
117
- /** A donde vuelve el pagador cuando el medio lo saca de la pagina (PSE, 3DS). */
118
- returnUrl?: string;
119
- /** Datos libres del comercio. Viajan en `meta`. */
120
- metadata?: Record<string, unknown>;
121
- }
122
- interface CardPaymentInput extends BasePaymentInput {
123
- method: 'card';
124
- cardToken: CardToken;
125
- }
126
- interface PsePaymentInput extends BasePaymentInput {
127
- method: 'pse';
128
- pse: PseData;
129
- }
130
- type PaymentInput = CardPaymentInput | PsePaymentInput;
131
- type PaymentStatus = 'approved' | 'pending' | 'declined' | 'failed' | 'cancelled' | 'refunded' | (string & {});
132
- interface PaymentResult {
133
- success: boolean;
134
- status: PaymentStatus;
135
- message?: string;
136
- transactionId: string | null;
137
- gatewayTransactionId?: string | null;
138
- /** PSE: URL del banco a la que hay que llevar al pagador. */
139
- redirectUrl?: string;
140
- declineReason?: string | null;
141
- amount?: number;
142
- currency?: string;
143
- reference?: string;
144
- [key: string]: unknown;
145
- }
146
- interface Bank {
147
- code: string;
148
- name: string;
149
- }
150
- declare function createPaymentsApi(http: HttpClient): {
151
- /** Crea el cobro. Para tarjeta, el `cardToken` sale de `card.tokenize()`. */
152
- create(input: PaymentInput): Promise<PaymentResult>;
153
- /**
154
- * Registra un rechazo en la tokenizacion como transaccion declinada, para
155
- * trazabilidad. No cobra nada ni lleva datos de tarjeta.
156
- */
157
- reportTokenizationDecline(input: Omit<CardPaymentInput, "cardToken">, decline: {
158
- code?: string;
159
- message?: string;
160
- }): Promise<PaymentResult>;
161
- /** Estado publico de una transaccion (el mismo recurso del comprobante). */
162
- status(transactionId: string): Promise<PaymentResult>;
163
- };
164
- declare function createPseApi(http: HttpClient): {
165
- /** Bancos PSE (Colombia). Se quita la entrada guia "seleccione su banco". */
166
- listBanks(currency?: string): Promise<Bank[]>;
167
- };
168
-
169
140
  interface CrosspayOptions {
170
141
  /** `development` → api-dev.crosspaysolutions.financial · `production` → api.crosspaysolutions.financial */
171
142
  environment: CrosspayEnvironment;
172
143
  /**
173
- * Id publico de la empresa (20 digitos). Opcional: si el dominio del
174
- * comercio esta registrado en el CORS de su empresa, el backend la infiere
175
- * del `Origin`.
144
+ * Llave publica del comercio (panel → Integracion). `pk_test_…` en
145
+ * development, `pk_live_…` en production. Es publica por diseno: solo
146
+ * funciona desde los dominios autorizados del comercio y con una sesion
147
+ * creada por su servidor.
176
148
  */
177
- companyId?: string;
149
+ publishableKey: string;
178
150
  /** Idioma de los correos al pagador (`es`, `en`…). */
179
151
  locale?: string;
180
152
  /** Para pruebas. Por defecto, el `fetch` del navegador. */
@@ -190,18 +162,13 @@ interface Crosspay {
190
162
  readonly environment: CrosspayEnvironment;
191
163
  card: {
192
164
  /**
193
- * Monta el formulario seguro de tarjeta en `options.container`. Rechaza
194
- * con `CARD_UNAVAILABLE` si la moneda no admite tarjeta.
165
+ * Monta el formulario seguro de tarjeta en `options.container` para la
166
+ * sesion `options.clientSecret`. Rechaza con `CrosspayError` si la llave,
167
+ * la sesion o el dominio no son validos.
195
168
  */
196
169
  mount(options: CardFieldsOptions): Promise<MountedCardFields>;
197
170
  };
198
- payments: ReturnType<typeof createPaymentsApi> & {
199
- /**
200
- * Atajo: tokeniza y cobra. Si la tarjeta se rechaza al tokenizar, deja
201
- * registrada la transaccion declinada y relanza el `CrosspayError`.
202
- */
203
- payWithCard(fields: MountedCardFields, input: Omit<CardPaymentInput, 'cardToken' | 'method'>): Promise<PaymentResult>;
204
- };
171
+ payments: ReturnType<typeof createPaymentsApi>;
205
172
  pse: ReturnType<typeof createPseApi>;
206
173
  }
207
174
  declare function init(options: CrosspayOptions): Crosspay;
@@ -222,4 +189,4 @@ declare class CrosspayError extends Error {
222
189
  /** Version de la libreria, inyectada en el build. */
223
190
  declare const version: string;
224
191
 
225
- export { API_BASE_URLS, type Bank, type CardFieldLabels, type CardFieldsOptions, type CardPaymentInput, type CardToken, type Crosspay, type CrosspayEnvironment, CrosspayError, type CrosspayOptions, type Customer, FIELDS_URLS, type MountedCardFields, type PaymentInput, type PaymentResult, type PaymentStatus, type PseData, type PsePaymentInput, init, version };
192
+ export { API_BASE_URLS, type Bank, type CardFieldLabels, type CardFieldsOptions, type Crosspay, type CrosspayEnvironment, CrosspayError, type CrosspayOptions, FIELDS_URLS, type MountedCardFields, type PaymentResult, type PaymentStatus, type PsePayInput, type SessionSummary, init, version };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- var p=class extends Error{constructor(r,s,o){super(r);this.code=s;this.details=o;this.name="CrosspayError"}};var f="crosspay-fields/1";function I(e,t){return typeof e=="object"&&e!==null&&e.protocol===f&&e.channel===t&&typeof e.kind=="string"}function w(e){return new URLSearchParams({channel:e.channel,origin:e.parentOrigin}).toString()}var $=3e4,A={position:"fixed",inset:"0",width:"100%",height:"100%",zIndex:"2147483647"};function x(e,t){let r=typeof t.container=="string"?document.querySelector(t.container):t.container;if(!r)return Promise.reject(new p(`No existe el contenedor ${String(t.container)}.`,"INVALID_CONTAINER"));let s=new URL(e).origin,o=v(),c=document.createElement("div");c.style.position="relative";let n=document.createElement("iframe");n.src=`${e}#${w({channel:o,parentOrigin:window.location.origin})}`,n.title="Formulario seguro de tarjeta",n.setAttribute("scrolling","no"),n.setAttribute("allow","payment"),Object.assign(n.style,{display:"block",width:"100%",height:"0px",border:"0",background:"transparent",colorScheme:"normal"}),c.appendChild(n),r.replaceChildren(c);let d=0,a=new Map,u=l=>{n.contentWindow?.postMessage({...l,protocol:f,channel:o},s)},M=l=>{l?(c.style.height=`${d}px`,Object.assign(n.style,A)):(c.style.height="",Object.assign(n.style,{position:"",inset:"",width:"100%",height:`${d}px`,zIndex:""}))},g=!1,k=()=>{if(!g){g=!0,window.removeEventListener("message",P);for(let l of a.values())l.reject(new p("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));a.clear(),c.remove()}},y=null;function P(l){if(l.source!==n.contentWindow||l.origin!==s||!I(l.data,o))return;let i=l.data;switch(i.kind){case"ready":u({kind:"init",options:{currency:t.currency,amount:t.amount,isSubscription:t.isSubscription,installments:t.installments,labels:t.labels,styles:t.styles}});break;case"mounted":y?.();break;case"mount-error":y?.(new p(i.message,i.code));break;case"resize":d=Math.max(0,Math.ceil(i.height)),n.style.position!=="fixed"?n.style.height=`${d}px`:c.style.height=`${d}px`;break;case"validity":t.onValidityChange?.(i.valid);break;case"overlay":M(i.active);break;case"token":{let m=a.get(i.requestId);a.delete(i.requestId),m?.resolve(i.token);break}case"token-error":{let m=a.get(i.requestId);a.delete(i.requestId),m?.reject(new p(i.message,i.code));break}}}window.addEventListener("message",P);let L={tokenize(){if(g)return Promise.reject(new p("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));let l=v();return new Promise((i,m)=>{a.set(l,{resolve:i,reject:m}),u({kind:"tokenize",requestId:l})})},destroy:k};return new Promise((l,i)=>{let m=setTimeout(()=>{y?.(new p("No se pudo cargar el formulario de tarjeta.","FIELDS_TIMEOUT"))},$);y=T=>{y=null,clearTimeout(m),T?(k(),i(T)):l(L)}})}function v(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`}var b={development:"https://api-dev.crosspaysolutions.financial",production:"https://api.crosspaysolutions.financial"};function E(e){let t=b[e];if(!t)throw new TypeError(`Ambiente de Crosspay no valido: "${String(e)}". Usa "development" o "production".`);return t}var h={development:"https://fields-dev.crosspaysolutions.financial/",production:"https://fields.crosspaysolutions.financial/"};function S(e){let t=(s,o)=>(e.fetch??globalThis.fetch)(s,o);async function r(s,o,c){let n={Accept:"application/json"};c!==void 0&&(n["Content-Type"]="application/json"),e.companyId&&(n["X-Company-Id"]=e.companyId),e.locale&&(n["Accept-Language"]=e.locale);let d;try{d=await t(`${e.baseUrl}${o}`,{method:s,headers:n,body:c===void 0?void 0:JSON.stringify(c),credentials:"omit"})}catch(u){throw new p("No se pudo conectar con Crosspay.","NETWORK_ERROR",u)}let a=await d.json().catch(()=>null);if(!d.ok||a?.success===!1){let u=a?.message||a?.error||`Crosspay respondio ${d.status}.`;throw new p(u,`HTTP_${d.status}`,a)}return a&&typeof a=="object"&&"data"in a?a.data:a}return{get:s=>r("GET",s),post:(s,o)=>r("POST",s,o)}}var O="cp_device_id";function _(){try{let e=localStorage.getItem(O);return e||(e=typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`,localStorage.setItem(O,e)),e}catch{return""}}function N(){return typeof navigator>"u"?"":[navigator.userAgent,navigator.language,typeof screen<"u"?`${screen.width}x${screen.height}`:"",C()].join("|")}function B(e){let t=5381;for(let r=0;r<e.length;r+=1)t=(t<<5)+t+e.charCodeAt(r)|0;return(t>>>0).toString(16)}function C(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch{return""}}function F(){let e=_(),t=B(N());return e?`${e}.${t}`:t}var j="crosspay.js";function R(e){let t=`${e.customer.firstName} ${e.customer.lastName}`.trim(),r=typeof navigator<"u"?navigator.language?.split("-")[0]:void 0;return{paymentMethod:e.method,amount:e.amount,currency:e.currency.toUpperCase(),description:e.description,reference:e.reference?.trim()||`REF-${e.method.toUpperCase()}-${Date.now()}`,customerEmail:e.customer.email,customerName:t,customer:e.customer,platform:j,..."cardToken"in e?{token:e.cardToken.token,...e.cardToken.deferred?{deferred:e.cardToken.deferred}:{}}:{},..."pse"in e?{pse:e.pse}:{},..."tokenizationDecline"in e?{tokenizationDecline:e.tokenizationDecline}:{},...e.returnUrl?{returnUrl:e.returnUrl}:{},meta:{...e.metadata??{},...r?{language:r}:{},fingerprint:F(),timezone:C()}}}function U(e){return{create(t){return e.post("/payments/process",R(t))},reportTokenizationDecline(t,r){return e.post("/payments/process",R({...t,tokenizationDecline:r}))},status(t){return e.get(`/payments/status/${encodeURIComponent(t)}`)}}}function D(e){return{async listBanks(t="COP"){return(await e.get(`/payments/pse/banks?currency=${encodeURIComponent(t)}`)??[]).filter(s=>{let o=String(s.code??"").trim(),c=String(s.name??"").normalize("NFD").replace(/\p{Diacritic}/gu,"").toLowerCase();return o!==""&&o!=="0"&&!c.includes("seleccione su banco")})}}}function z(e){let t=S({baseUrl:E(e.environment),companyId:e.companyId,locale:e.locale,fetch:e.fetch});if(e.fieldsUrl&&e.environment!=="development")throw new TypeError('`fieldsUrl` solo se admite en el ambiente "development".');let r=e.fieldsUrl??h[e.environment],s=U(t);return{environment:e.environment,card:{async mount(o){if(!o.isSubscription&&!(Number(o.amount)>0))throw new p("`amount` debe ser mayor a 0.","INVALID_AMOUNT");return x(r,o)}},payments:{...s,async payWithCard(o,c){let n;try{n=await o.tokenize()}catch(d){throw d instanceof p&&await s.reportTokenizationDecline({...c,method:"card"},{code:d.code,message:d.message}).catch(()=>{}),d}return s.create({...c,method:"card",cardToken:n})}},pse:D(t)}}var ae="0.1.0";export{b as API_BASE_URLS,p as CrosspayError,h as FIELDS_URLS,z as init,ae as version};
1
+ var c=class extends Error{constructor(t,d,s){super(t);this.code=d;this.details=s;this.name="CrosspayError"}};var v="crosspay-fields/2";function x(e,r){return typeof e=="object"&&e!==null&&e.protocol===v&&e.channel===r&&typeof e.kind=="string"}function k(e){return new URLSearchParams({channel:e.channel,origin:e.parentOrigin}).toString()}var D=3e4,j={position:"fixed",inset:"0",width:"100%",height:"100%",zIndex:"2147483647"};function T(e,r,t){let d=typeof t.container=="string"?document.querySelector(t.container):t.container;if(!d)return Promise.reject(new c(`No existe el contenedor ${String(t.container)}.`,"INVALID_CONTAINER"));let s=new URL(e).origin,o=P(),i=document.createElement("div");i.style.position="relative";let n=document.createElement("iframe");n.src=`${e}#${k({channel:o,parentOrigin:window.location.origin})}`,n.title="Formulario seguro de tarjeta",n.setAttribute("scrolling","no"),n.setAttribute("allow","payment"),Object.assign(n.style,{display:"block",width:"100%",height:"0px",border:"0",background:"transparent",colorScheme:"normal"}),i.appendChild(n),d.replaceChildren(i);let p=0,l=new Map,u=m=>{n.contentWindow?.postMessage({...m,protocol:v,channel:o},s)},_=m=>{m?(i.style.height=`${p}px`,Object.assign(n.style,j)):(i.style.height="",Object.assign(n.style,{position:"",inset:"",width:"100%",height:`${p}px`,zIndex:""}))},f=!1,C=()=>{if(!f){f=!0,window.removeEventListener("message",E);for(let m of l.values())m.reject(new c("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));l.clear(),i.remove()}},y=null;function E(m){if(m.source!==n.contentWindow||m.origin!==s||!x(m.data,o))return;let a=m.data;switch(a.kind){case"ready":u({kind:"init",options:{publishableKey:r,clientSecret:t.clientSecret,installments:t.installments,labels:t.labels,styles:t.styles}});break;case"mounted":y?.(a.session);break;case"mount-error":y?.(new c(a.message,a.code));break;case"resize":p=Math.max(0,Math.ceil(a.height)),n.style.position!=="fixed"?n.style.height=`${p}px`:i.style.height=`${p}px`;break;case"validity":t.onValidityChange?.(a.valid);break;case"overlay":_(a.active);break;case"paid":{let g=l.get(a.requestId);l.delete(a.requestId),g?.resolve(a.result);break}case"pay-error":{let g=l.get(a.requestId);l.delete(a.requestId),g?.reject(new c(a.message,a.code));break}}}return window.addEventListener("message",E),new Promise((m,a)=>{let g=setTimeout(()=>{y?.(new c("No se pudo cargar el formulario de tarjeta.","FIELDS_TIMEOUT"))},D);y=h=>{if(y=null,clearTimeout(g),h instanceof c){C(),a(h);return}m({session:h,pay(){if(f)return Promise.reject(new c("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));let b=P();return new Promise(($,A)=>{l.set(b,{resolve:$,reject:A}),u({kind:"pay",requestId:b})})},destroy:C})}})}function P(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`}var I={development:"https://api-dev.crosspaysolutions.financial",production:"https://api.crosspaysolutions.financial"};function R(e){let r=I[e];if(!r)throw new TypeError(`Ambiente de Crosspay no valido: "${String(e)}". Usa "development" o "production".`);return r}var S={development:"https://fields-dev.crosspaysolutions.financial/",production:"https://fields.crosspaysolutions.financial/"};function O(e){let r=(s,o)=>(e.fetch??globalThis.fetch)(s,o);async function t(s,o,i){let n={Accept:"application/json"};i!==void 0&&(n["Content-Type"]="application/json"),e.locale&&(n["Accept-Language"]=e.locale);let p;try{p=await r(`${e.baseUrl}${o}`,{method:s,headers:n,body:i===void 0?void 0:JSON.stringify(i),credentials:"omit"})}catch(u){throw new c("No se pudo conectar con Crosspay.","NETWORK_ERROR",u)}let l=await p.json().catch(()=>null);if(!p.ok||l?.success===!1){let u=l?.message||l?.error||`Crosspay respondio ${p.status}.`;throw new c(u,`HTTP_${p.status}`,l)}return l}async function d(s,o,i){let n=await t(s,o,i);return n&&typeof n=="object"&&"data"in n?n.data:n}return{get:s=>d("GET",s),post:(s,o)=>d("POST",s,o),postRaw:(s,o)=>t("POST",s,o)}}var F="cp_device_id";function B(){try{let e=localStorage.getItem(F);return e||(e=typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`,localStorage.setItem(F,e)),e}catch{return""}}function q(){return typeof navigator>"u"?"":[navigator.userAgent,navigator.language,typeof screen<"u"?`${screen.width}x${screen.height}`:"",w()].join("|")}function N(e){let r=5381;for(let t=0;t<e.length;t+=1)r=(r<<5)+r+e.charCodeAt(t)|0;return(r>>>0).toString(16)}function w(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch{return""}}function U(){let e=B(),r=N(q());return e?`${e}.${r}`:r}function H(){let e=typeof navigator<"u"?navigator.language?.split("-")[0]:void 0;return{fingerprint:U(),timezone:w(),...e?{language:e}:{}}}function L(e){return{status(r){return e.get(`/payments/status/${encodeURIComponent(r)}`)}}}function M(e,r){return{async listBanks(t="COP"){return(await e.get(`/payments/pse/banks?currency=${encodeURIComponent(t)}`)??[]).filter(s=>{let o=String(s.code??"").trim(),i=String(s.name??"").normalize("NFD").replace(/\p{Diacritic}/gu,"").toLowerCase();return o!==""&&o!=="0"&&!i.includes("seleccione su banco")})},async pay(t){let{clientSecret:d,...s}=t,o=await e.postRaw("/payments/sessions/pay",{publishableKey:r,clientSecret:d,parentOrigin:window.location.origin,method:"pse",pse:s,meta:H()}),{data:i,...n}=o??{},p=i?.sessionId;return{...n,...p?{sessionId:p}:{}}}}}var z={development:"pk_test_",production:"pk_live_"};function K(e){let r=R(e.environment),t=String(e.publishableKey??"").trim(),d=z[e.environment];if(!t.startsWith(d))throw new TypeError(`\`publishableKey\` debe empezar por "${d}" en el ambiente "${e.environment}".`);if(e.fieldsUrl&&e.environment!=="development")throw new TypeError('`fieldsUrl` solo se admite en el ambiente "development".');let s=e.fieldsUrl??S[e.environment],o=O({baseUrl:r,locale:e.locale,fetch:e.fetch});return{environment:e.environment,card:{async mount(i){if(!String(i.clientSecret??"").trim())throw new c("Falta `clientSecret`: crea la sesion de pago en tu servidor con @crosspay-lb/node.","INVALID_REQUEST");return T(s,t,i)}},payments:L(o),pse:M(o,t)}}var pe="0.2.0";export{I as API_BASE_URLS,c as CrosspayError,S as FIELDS_URLS,K as init,pe as version};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crosspay-lb/js",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Librería de navegador de Crosspay: formulario seguro de tarjeta, PSE y estado de pagos.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",