@crosspay-lb/js 0.3.0 → 0.4.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 +14 -1
- package/dist/crosspay.min.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +51 -8
- package/dist/index.d.ts +51 -8
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,20 @@ const fields = await cp.card.mount({ container: '#card', clientSecret })
|
|
|
17
17
|
const result = await fields.pay()
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
Si la orden se crea recién al pagar, monta el formulario antes con el monto y la
|
|
21
|
+
moneda, y pasa la sesión en `pay` (desde 0.4.0, solo cobro único):
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
const fields = await cp.card.mountDeferred({ container: '#card', amount: 50000, currency: 'COP' })
|
|
25
|
+
// …el pagador llena la tarjeta y le da a pagar:
|
|
26
|
+
const { clientSecret } = await crearOrdenEnTuServidor()
|
|
27
|
+
const result = await fields.pay({ clientSecret })
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Si la sesión no es de ese monto y moneda, `pay` rechaza con `AMOUNT_MISMATCH`
|
|
31
|
+
sin tocar la tarjeta: cuando el total cambie, `destroy()` y vuelve a montar.
|
|
32
|
+
|
|
33
|
+
O por CDN: `<script src="https://cdn.jsdelivr.net/npm/@crosspay-lb/js@0.4/dist/crosspay.min.js"></script>` → `window.Crosspay`.
|
|
21
34
|
|
|
22
35
|
Requisitos: llave pública del comercio, sitio en sus **dominios autorizados** y
|
|
23
36
|
una sesión de pago creada en el servidor con la llave secreta.
|
package/dist/crosspay.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var Crosspay=(()=>{var v=Object.defineProperty;var
|
|
1
|
+
"use strict";var Crosspay=(()=>{var v=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var H=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var V=(e,t)=>{for(var n in t)v(e,n,{get:t[n],enumerable:!0})},K=(e,t,n,d)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of H(t))!z.call(e,s)&&s!==n&&v(e,s,{get:()=>t[s],enumerable:!(d=q(t,s))||d.enumerable});return e};var Y=e=>K(v({},"__esModule",{value:!0}),e);var ne={};V(ne,{API_BASE_URLS:()=>S,CrosspayError:()=>i,init:()=>P,version:()=>j});var i=class extends Error{constructor(n,d,s){super(n);this.code=d;this.details=s;this.name="CrosspayError"}};var C="crosspay-fields/2";function R(e,t){return typeof e=="object"&&e!==null&&e.protocol===C&&e.channel===t&&typeof e.kind=="string"}function k(e){return new URLSearchParams({channel:e.channel,origin:e.parentOrigin}).toString()}var Q=3e4,W={position:"fixed",inset:"0",width:"100%",height:"100%",zIndex:"2147483647"};function b(e,t,n){let d=typeof n.container=="string"?document.querySelector(n.container):n.container;if(!d)return Promise.reject(new i(`No existe el contenedor ${String(n.container)}.`,"INVALID_CONTAINER"));let s=new URL(e).origin,a=D(),o=document.createElement("div");o.style.position="relative";let r=document.createElement("iframe");r.src=`${e}#${k({channel:a,parentOrigin:window.location.origin})}`,r.title="Formulario seguro de tarjeta",r.setAttribute("scrolling","no"),r.setAttribute("allow","payment"),Object.assign(r.style,{display:"block",width:"100%",height:"0px",border:"0",background:"transparent",colorScheme:"normal"}),o.appendChild(r),d.replaceChildren(o);let c=0,p=new Map,m=u=>{r.contentWindow?.postMessage({...u,protocol:C,channel:a},s)},f=u=>{u?(o.style.height=`${c}px`,Object.assign(r.style,W)):(o.style.height="",Object.assign(r.style,{position:"",inset:"",width:"100%",height:`${c}px`,zIndex:""}))},h=!1,x=()=>{if(!h){h=!0,window.removeEventListener("message",O);for(let u of p.values())u.reject(new i("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));p.clear(),o.remove()}},y=null;function O(u){if(u.source!==r.contentWindow||u.origin!==s||!R(u.data,a))return;let l=u.data;switch(l.kind){case"ready":m({kind:"init",options:{publishableKey:t,clientSecret:n.clientSecret,amount:n.amount,currency:n.currency,installments:n.installments,labels:n.labels,styles:n.styles}});break;case"mounted":y?.({session:l.session});break;case"mount-error":y?.(new i(l.message,l.code));break;case"resize":c=Math.max(0,Math.ceil(l.height)),r.style.position!=="fixed"?r.style.height=`${c}px`:o.style.height=`${c}px`;break;case"validity":n.onValidityChange?.(l.valid);break;case"overlay":f(l.active);break;case"paid":{let g=p.get(l.requestId);p.delete(l.requestId),g?.resolve(l.result);break}case"pay-error":{let g=p.get(l.requestId);p.delete(l.requestId),g?.reject(new i(l.message,l.code));break}}}return window.addEventListener("message",O),new Promise((u,l)=>{let g=setTimeout(()=>{y?.(new i("No se pudo cargar el formulario de tarjeta.","FIELDS_TIMEOUT"))},Q);y=w=>{if(y=null,clearTimeout(g),w instanceof i){x(),l(w);return}u({session:w.session,pay(F){if(h)return Promise.reject(new i("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));let T=D();return new Promise((N,B)=>{p.set(T,{resolve:N,reject:B}),m({kind:"pay",requestId:T,...F?{clientSecret:F}:{}})})},destroy:x})}})}function D(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`}var S={development:"https://api-dev.crosspaysolutions.financial",production:"https://api.crosspaysolutions.financial"};function U(e){let t=S[e];if(!t)throw new TypeError(`Ambiente de Crosspay no valido: "${String(e)}". Usa "development" o "production".`);return t}var E={development:"https://fields-dev.crosspaysolutions.financial/",production:"https://fields.crosspaysolutions.financial/"};function _(e){let t=(s,a)=>(e.fetch??globalThis.fetch)(s,a);async function n(s,a,o){let r={Accept:"application/json"};o!==void 0&&(r["Content-Type"]="application/json"),e.locale&&(r["Accept-Language"]=e.locale);let c;try{c=await t(`${e.baseUrl}${a}`,{method:s,headers:r,body:o===void 0?void 0:JSON.stringify(o),credentials:"omit"})}catch(m){throw new i("No se pudo conectar con Crosspay.","NETWORK_ERROR",m)}let p=await c.json().catch(()=>null);if(!c.ok||p?.success===!1){let m=p?.message||p?.error||`Crosspay respondio ${c.status}.`;throw new i(m,`HTTP_${c.status}`,p)}return p}async function d(s,a,o){let r=await n(s,a,o);return r&&typeof r=="object"&&"data"in r?r.data:r}return{get:s=>d("GET",s),post:(s,a)=>d("POST",s,a),postRaw:(s,a)=>n("POST",s,a)}}var L="cp_device_id";function G(){try{let e=localStorage.getItem(L);return e||(e=typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`,localStorage.setItem(L,e)),e}catch{return""}}function Z(){return typeof navigator>"u"?"":[navigator.userAgent,navigator.language,typeof screen<"u"?`${screen.width}x${screen.height}`:"",I()].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 I(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch{return""}}function M(){let e=G(),t=J(Z());return e?`${e}.${t}`:t}function X(){let e=typeof navigator<"u"?navigator.language?.split("-")[0]:void 0;return{fingerprint:M(),timezone:I(),...e?{language:e}:{}}}function A(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(s=>{let a=String(s.code??"").trim(),o=String(s.name??"").normalize("NFD").replace(/\p{Diacritic}/gu,"").toLowerCase();return a!==""&&a!=="0"&&!o.includes("seleccione su banco")})},async pay(n){let{clientSecret:d,...s}=n,a=await e.postRaw("/payments/sessions/pay",{publishableKey:t,clientSecret:d,parentOrigin:window.location.origin,method:"pse",pse:s,meta:X()}),{data:o,...r}=a??{},c=o?.sessionId;return{...r,...c?{sessionId:c}:{}}}}}var ee={development:"pk_test_",production:"pk_live_"};function P(e){let t=U(e.environment),n=String(e.publishableKey??"").trim(),d=ee[e.environment];if(!n.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??E[e.environment],a=_({baseUrl:t,locale:e.locale,fetch:e.fetch});return{environment:e.environment,card:{async mount(o){if(!String(o.clientSecret??"").trim())throw new i("Falta `clientSecret`: crea la sesion de pago en tu servidor con @crosspay-lb/node.","INVALID_REQUEST");let r=await b(s,n,o);return{session:r.session,pay:()=>r.pay(),destroy:r.destroy}},async mountDeferred(o){let{amount:r,currency:c}=te(o),p=await b(s,n,{...o,amount:r,currency:c});return{amount:r,currency:c,pay(m){let f=String(m?.clientSecret??"").trim();return f?p.pay(f):Promise.reject(new i("Falta `clientSecret`: crea la sesion de pago en tu servidor antes de pagar.","INVALID_REQUEST"))},destroy:p.destroy}}},payments:A(a),pse:$(a,n)}}function te(e){if("clientSecret"in e)throw new i("`mountDeferred` no recibe `clientSecret`: pasalo en `pay()`, o usa `mount` si ya tienes la sesion.","INVALID_REQUEST");let t=Number(e.amount);if(!Number.isFinite(t)||t<=0)throw new i("`amount` debe ser un numero mayor que cero.","INVALID_REQUEST");let n=String(e.currency??"").trim().toUpperCase();if(!/^[A-Z]{3}$/.test(n))throw new i('`currency` debe ser un codigo ISO 4217, p. ej. "COP".',"INVALID_REQUEST");return{amount:t,currency:n}}var j="0.4.0";return Y(ne);})();
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var v=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var
|
|
1
|
+
"use strict";var v=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var H=Object.prototype.hasOwnProperty;var z=(e,t)=>{for(var n in t)v(e,n,{get:t[n],enumerable:!0})},V=(e,t,n,d)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of q(t))!H.call(e,s)&&s!==n&&v(e,s,{get:()=>t[s],enumerable:!(d=B(t,s))||d.enumerable});return e};var K=e=>V(v({},"__esModule",{value:!0}),e);var ne={};z(ne,{API_BASE_URLS:()=>E,CrosspayError:()=>a,FIELDS_URLS:()=>h,init:()=>$,version:()=>te});module.exports=K(ne);var a=class extends Error{constructor(n,d,s){super(n);this.code=d;this.details=s;this.name="CrosspayError"}};var C="crosspay-fields/2";function T(e,t){return typeof e=="object"&&e!==null&&e.protocol===C&&e.channel===t&&typeof e.kind=="string"}function k(e){return new URLSearchParams({channel:e.channel,origin:e.parentOrigin}).toString()}var Y=3e4,Q={position:"fixed",inset:"0",width:"100%",height:"100%",zIndex:"2147483647"};function b(e,t,n){let d=typeof n.container=="string"?document.querySelector(n.container):n.container;if(!d)return Promise.reject(new a(`No existe el contenedor ${String(n.container)}.`,"INVALID_CONTAINER"));let s=new URL(e).origin,i=R(),o=document.createElement("div");o.style.position="relative";let r=document.createElement("iframe");r.src=`${e}#${k({channel:i,parentOrigin:window.location.origin})}`,r.title="Formulario seguro de tarjeta",r.setAttribute("scrolling","no"),r.setAttribute("allow","payment"),Object.assign(r.style,{display:"block",width:"100%",height:"0px",border:"0",background:"transparent",colorScheme:"normal"}),o.appendChild(r),d.replaceChildren(o);let c=0,p=new Map,m=u=>{r.contentWindow?.postMessage({...u,protocol:C,channel:i},s)},f=u=>{u?(o.style.height=`${c}px`,Object.assign(r.style,Q)):(o.style.height="",Object.assign(r.style,{position:"",inset:"",width:"100%",height:`${c}px`,zIndex:""}))},S=!1,P=()=>{if(!S){S=!0,window.removeEventListener("message",x);for(let u of p.values())u.reject(new a("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));p.clear(),o.remove()}},y=null;function x(u){if(u.source!==r.contentWindow||u.origin!==s||!T(u.data,i))return;let l=u.data;switch(l.kind){case"ready":m({kind:"init",options:{publishableKey:t,clientSecret:n.clientSecret,amount:n.amount,currency:n.currency,installments:n.installments,labels:n.labels,styles:n.styles}});break;case"mounted":y?.({session:l.session});break;case"mount-error":y?.(new a(l.message,l.code));break;case"resize":c=Math.max(0,Math.ceil(l.height)),r.style.position!=="fixed"?r.style.height=`${c}px`:o.style.height=`${c}px`;break;case"validity":n.onValidityChange?.(l.valid);break;case"overlay":f(l.active);break;case"paid":{let g=p.get(l.requestId);p.delete(l.requestId),g?.resolve(l.result);break}case"pay-error":{let g=p.get(l.requestId);p.delete(l.requestId),g?.reject(new a(l.message,l.code));break}}}return window.addEventListener("message",x),new Promise((u,l)=>{let g=setTimeout(()=>{y?.(new a("No se pudo cargar el formulario de tarjeta.","FIELDS_TIMEOUT"))},Y);y=w=>{if(y=null,clearTimeout(g),w instanceof a){P(),l(w);return}u({session:w.session,pay(O){if(S)return Promise.reject(new a("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));let F=R();return new Promise((j,N)=>{p.set(F,{resolve:j,reject:N}),m({kind:"pay",requestId:F,...O?{clientSecret:O}:{}})})},destroy:P})}})}function R(){return typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`}var E={development:"https://api-dev.crosspaysolutions.financial",production:"https://api.crosspaysolutions.financial"};function D(e){let t=E[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 U(e){let t=(s,i)=>(e.fetch??globalThis.fetch)(s,i);async function n(s,i,o){let r={Accept:"application/json"};o!==void 0&&(r["Content-Type"]="application/json"),e.locale&&(r["Accept-Language"]=e.locale);let c;try{c=await t(`${e.baseUrl}${i}`,{method:s,headers:r,body:o===void 0?void 0:JSON.stringify(o),credentials:"omit"})}catch(m){throw new a("No se pudo conectar con Crosspay.","NETWORK_ERROR",m)}let p=await c.json().catch(()=>null);if(!c.ok||p?.success===!1){let m=p?.message||p?.error||`Crosspay respondio ${c.status}.`;throw new a(m,`HTTP_${c.status}`,p)}return p}async function d(s,i,o){let r=await n(s,i,o);return r&&typeof r=="object"&&"data"in r?r.data:r}return{get:s=>d("GET",s),post:(s,i)=>d("POST",s,i),postRaw:(s,i)=>n("POST",s,i)}}var L="cp_device_id";function W(){try{let e=localStorage.getItem(L);return e||(e=typeof crypto<"u"&&"randomUUID"in crypto?crypto.randomUUID():`${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`,localStorage.setItem(L,e)),e}catch{return""}}function G(){return typeof navigator>"u"?"":[navigator.userAgent,navigator.language,typeof screen<"u"?`${screen.width}x${screen.height}`:"",I()].join("|")}function Z(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 I(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch{return""}}function _(){let e=W(),t=Z(G());return e?`${e}.${t}`:t}function J(){let e=typeof navigator<"u"?navigator.language?.split("-")[0]:void 0;return{fingerprint:_(),timezone:I(),...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(s=>{let i=String(s.code??"").trim(),o=String(s.name??"").normalize("NFD").replace(/\p{Diacritic}/gu,"").toLowerCase();return i!==""&&i!=="0"&&!o.includes("seleccione su banco")})},async pay(n){let{clientSecret:d,...s}=n,i=await e.postRaw("/payments/sessions/pay",{publishableKey:t,clientSecret:d,parentOrigin:window.location.origin,method:"pse",pse:s,meta:J()}),{data:o,...r}=i??{},c=o?.sessionId;return{...r,...c?{sessionId:c}:{}}}}}var X={development:"pk_test_",production:"pk_live_"};function $(e){let t=D(e.environment),n=String(e.publishableKey??"").trim(),d=X[e.environment];if(!n.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??h[e.environment],i=U({baseUrl:t,locale:e.locale,fetch:e.fetch});return{environment:e.environment,card:{async mount(o){if(!String(o.clientSecret??"").trim())throw new a("Falta `clientSecret`: crea la sesion de pago en tu servidor con @crosspay-lb/node.","INVALID_REQUEST");let r=await b(s,n,o);return{session:r.session,pay:()=>r.pay(),destroy:r.destroy}},async mountDeferred(o){let{amount:r,currency:c}=ee(o),p=await b(s,n,{...o,amount:r,currency:c});return{amount:r,currency:c,pay(m){let f=String(m?.clientSecret??"").trim();return f?p.pay(f):Promise.reject(new a("Falta `clientSecret`: crea la sesion de pago en tu servidor antes de pagar.","INVALID_REQUEST"))},destroy:p.destroy}}},payments:M(i),pse:A(i,n)}}function ee(e){if("clientSecret"in e)throw new a("`mountDeferred` no recibe `clientSecret`: pasalo en `pay()`, o usa `mount` si ya tienes la sesion.","INVALID_REQUEST");let t=Number(e.amount);if(!Number.isFinite(t)||t<=0)throw new a("`amount` debe ser un numero mayor que cero.","INVALID_REQUEST");let n=String(e.currency??"").trim().toUpperCase();if(!/^[A-Z]{3}$/.test(n))throw new a('`currency` debe ser un codigo ISO 4217, p. ej. "COP".',"INVALID_REQUEST");return{amount:t,currency:n}}var te="0.4.0";0&&(module.exports={API_BASE_URLS,CrosspayError,FIELDS_URLS,init,version});
|
package/dist/index.d.cts
CHANGED
|
@@ -102,15 +102,10 @@ interface CardFieldLabels {
|
|
|
102
102
|
cvv?: string;
|
|
103
103
|
installments?: string;
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
/** Opciones comunes a los dos modos de montaje. */
|
|
106
|
+
interface BaseCardFieldsOptions {
|
|
106
107
|
/** Donde se inserta el formulario: un selector CSS o el elemento. */
|
|
107
108
|
container: string | HTMLElement;
|
|
108
|
-
/**
|
|
109
|
-
* `clientSecret` de la sesion de pago que creo el SERVIDOR del comercio con
|
|
110
|
-
* su llave secreta (`sessions.create` de @crosspay-lb/node). Monto, moneda y
|
|
111
|
-
* cliente salen de la sesion: el navegador no puede cambiarlos.
|
|
112
|
-
*/
|
|
113
|
-
clientSecret: string;
|
|
114
109
|
/** Muestra el selector de cuotas. */
|
|
115
110
|
installments?: boolean;
|
|
116
111
|
labels?: CardFieldLabels;
|
|
@@ -123,6 +118,48 @@ interface CardFieldsOptions {
|
|
|
123
118
|
/** Se dispara cada vez que cambia la validez del formulario. */
|
|
124
119
|
onValidityChange?: (isValid: boolean) => void;
|
|
125
120
|
}
|
|
121
|
+
interface CardFieldsOptions extends BaseCardFieldsOptions {
|
|
122
|
+
/**
|
|
123
|
+
* `clientSecret` de la sesion de pago que creo el SERVIDOR del comercio con
|
|
124
|
+
* su llave secreta (`sessions.create` de @crosspay-lb/node). Monto, moneda y
|
|
125
|
+
* cliente salen de la sesion: el navegador no puede cambiarlos.
|
|
126
|
+
*/
|
|
127
|
+
clientSecret: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Montaje diferido (0.4.0): el formulario aparece antes de que exista la
|
|
131
|
+
* sesion, y la sesion se pasa en `pay()`. Sirve para crear la orden recien
|
|
132
|
+
* cuando el pagador le da a pagar.
|
|
133
|
+
*
|
|
134
|
+
* `amount` y `currency` son los que vera el banco en la verificacion 3DS. NO
|
|
135
|
+
* deciden el cobro: al pagar, la pagina de campos compara con la sesion y, si
|
|
136
|
+
* no coinciden, rechaza con `AMOUNT_MISMATCH` sin tocar la tarjeta. Si el total
|
|
137
|
+
* cambia (cupon, envio…), retira el formulario y vuelve a montarlo.
|
|
138
|
+
*
|
|
139
|
+
* Solo cobro unico: una sesion de suscripcion se monta con `clientSecret`.
|
|
140
|
+
*/
|
|
141
|
+
interface DeferredCardFieldsOptions extends BaseCardFieldsOptions {
|
|
142
|
+
amount: number;
|
|
143
|
+
/** ISO 4217, p. ej. `COP` o `USD`. */
|
|
144
|
+
currency: string;
|
|
145
|
+
}
|
|
146
|
+
/** Lo que `pay()` de un montaje diferido necesita. */
|
|
147
|
+
interface DeferredPayInput {
|
|
148
|
+
/** `clientSecret` de la sesion que el servidor acaba de crear para este cobro. */
|
|
149
|
+
clientSecret: string;
|
|
150
|
+
}
|
|
151
|
+
interface DeferredCardFields {
|
|
152
|
+
/** Monto y moneda con que se monto, en mayusculas. */
|
|
153
|
+
readonly amount: number;
|
|
154
|
+
readonly currency: string;
|
|
155
|
+
/**
|
|
156
|
+
* Valida la sesion, tokeniza y cobra. Lanza `CrosspayError` con
|
|
157
|
+
* `AMOUNT_MISMATCH` si la sesion no es del monto y moneda montados.
|
|
158
|
+
*/
|
|
159
|
+
pay(input: DeferredPayInput): Promise<PaymentResult>;
|
|
160
|
+
/** Retira el formulario. */
|
|
161
|
+
destroy(): void;
|
|
162
|
+
}
|
|
126
163
|
interface MountedCardFields {
|
|
127
164
|
/** Monto, moneda y descripcion de la sesion que se va a pagar. */
|
|
128
165
|
readonly session: SessionSummary;
|
|
@@ -183,6 +220,12 @@ interface Crosspay {
|
|
|
183
220
|
* la sesion o el dominio no son validos.
|
|
184
221
|
*/
|
|
185
222
|
mount(options: CardFieldsOptions): Promise<MountedCardFields>;
|
|
223
|
+
/**
|
|
224
|
+
* Monta el formulario ANTES de que exista la sesion, con el monto y la
|
|
225
|
+
* moneda que se van a cobrar; la sesion se pasa en `pay({ clientSecret })`.
|
|
226
|
+
* Rechaza con `CrosspayError` si la llave o el dominio no son validos.
|
|
227
|
+
*/
|
|
228
|
+
mountDeferred(options: DeferredCardFieldsOptions): Promise<DeferredCardFields>;
|
|
186
229
|
};
|
|
187
230
|
payments: ReturnType<typeof createPaymentsApi>;
|
|
188
231
|
pse: ReturnType<typeof createPseApi>;
|
|
@@ -205,4 +248,4 @@ declare class CrosspayError extends Error {
|
|
|
205
248
|
/** Version de la libreria, inyectada en el build. */
|
|
206
249
|
declare const version: string;
|
|
207
250
|
|
|
208
|
-
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 };
|
|
251
|
+
export { API_BASE_URLS, type Bank, type CardFieldLabels, type CardFieldsOptions, type Crosspay, type CrosspayEnvironment, CrosspayError, type CrosspayOptions, type DeferredCardFields, type DeferredCardFieldsOptions, type DeferredPayInput, FIELDS_URLS, type MountedCardFields, type PaymentResult, type PaymentStatus, type PsePayInput, type SessionSummary, init, version };
|
package/dist/index.d.ts
CHANGED
|
@@ -102,15 +102,10 @@ interface CardFieldLabels {
|
|
|
102
102
|
cvv?: string;
|
|
103
103
|
installments?: string;
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
/** Opciones comunes a los dos modos de montaje. */
|
|
106
|
+
interface BaseCardFieldsOptions {
|
|
106
107
|
/** Donde se inserta el formulario: un selector CSS o el elemento. */
|
|
107
108
|
container: string | HTMLElement;
|
|
108
|
-
/**
|
|
109
|
-
* `clientSecret` de la sesion de pago que creo el SERVIDOR del comercio con
|
|
110
|
-
* su llave secreta (`sessions.create` de @crosspay-lb/node). Monto, moneda y
|
|
111
|
-
* cliente salen de la sesion: el navegador no puede cambiarlos.
|
|
112
|
-
*/
|
|
113
|
-
clientSecret: string;
|
|
114
109
|
/** Muestra el selector de cuotas. */
|
|
115
110
|
installments?: boolean;
|
|
116
111
|
labels?: CardFieldLabels;
|
|
@@ -123,6 +118,48 @@ interface CardFieldsOptions {
|
|
|
123
118
|
/** Se dispara cada vez que cambia la validez del formulario. */
|
|
124
119
|
onValidityChange?: (isValid: boolean) => void;
|
|
125
120
|
}
|
|
121
|
+
interface CardFieldsOptions extends BaseCardFieldsOptions {
|
|
122
|
+
/**
|
|
123
|
+
* `clientSecret` de la sesion de pago que creo el SERVIDOR del comercio con
|
|
124
|
+
* su llave secreta (`sessions.create` de @crosspay-lb/node). Monto, moneda y
|
|
125
|
+
* cliente salen de la sesion: el navegador no puede cambiarlos.
|
|
126
|
+
*/
|
|
127
|
+
clientSecret: string;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Montaje diferido (0.4.0): el formulario aparece antes de que exista la
|
|
131
|
+
* sesion, y la sesion se pasa en `pay()`. Sirve para crear la orden recien
|
|
132
|
+
* cuando el pagador le da a pagar.
|
|
133
|
+
*
|
|
134
|
+
* `amount` y `currency` son los que vera el banco en la verificacion 3DS. NO
|
|
135
|
+
* deciden el cobro: al pagar, la pagina de campos compara con la sesion y, si
|
|
136
|
+
* no coinciden, rechaza con `AMOUNT_MISMATCH` sin tocar la tarjeta. Si el total
|
|
137
|
+
* cambia (cupon, envio…), retira el formulario y vuelve a montarlo.
|
|
138
|
+
*
|
|
139
|
+
* Solo cobro unico: una sesion de suscripcion se monta con `clientSecret`.
|
|
140
|
+
*/
|
|
141
|
+
interface DeferredCardFieldsOptions extends BaseCardFieldsOptions {
|
|
142
|
+
amount: number;
|
|
143
|
+
/** ISO 4217, p. ej. `COP` o `USD`. */
|
|
144
|
+
currency: string;
|
|
145
|
+
}
|
|
146
|
+
/** Lo que `pay()` de un montaje diferido necesita. */
|
|
147
|
+
interface DeferredPayInput {
|
|
148
|
+
/** `clientSecret` de la sesion que el servidor acaba de crear para este cobro. */
|
|
149
|
+
clientSecret: string;
|
|
150
|
+
}
|
|
151
|
+
interface DeferredCardFields {
|
|
152
|
+
/** Monto y moneda con que se monto, en mayusculas. */
|
|
153
|
+
readonly amount: number;
|
|
154
|
+
readonly currency: string;
|
|
155
|
+
/**
|
|
156
|
+
* Valida la sesion, tokeniza y cobra. Lanza `CrosspayError` con
|
|
157
|
+
* `AMOUNT_MISMATCH` si la sesion no es del monto y moneda montados.
|
|
158
|
+
*/
|
|
159
|
+
pay(input: DeferredPayInput): Promise<PaymentResult>;
|
|
160
|
+
/** Retira el formulario. */
|
|
161
|
+
destroy(): void;
|
|
162
|
+
}
|
|
126
163
|
interface MountedCardFields {
|
|
127
164
|
/** Monto, moneda y descripcion de la sesion que se va a pagar. */
|
|
128
165
|
readonly session: SessionSummary;
|
|
@@ -183,6 +220,12 @@ interface Crosspay {
|
|
|
183
220
|
* la sesion o el dominio no son validos.
|
|
184
221
|
*/
|
|
185
222
|
mount(options: CardFieldsOptions): Promise<MountedCardFields>;
|
|
223
|
+
/**
|
|
224
|
+
* Monta el formulario ANTES de que exista la sesion, con el monto y la
|
|
225
|
+
* moneda que se van a cobrar; la sesion se pasa en `pay({ clientSecret })`.
|
|
226
|
+
* Rechaza con `CrosspayError` si la llave o el dominio no son validos.
|
|
227
|
+
*/
|
|
228
|
+
mountDeferred(options: DeferredCardFieldsOptions): Promise<DeferredCardFields>;
|
|
186
229
|
};
|
|
187
230
|
payments: ReturnType<typeof createPaymentsApi>;
|
|
188
231
|
pse: ReturnType<typeof createPseApi>;
|
|
@@ -205,4 +248,4 @@ declare class CrosspayError extends Error {
|
|
|
205
248
|
/** Version de la libreria, inyectada en el build. */
|
|
206
249
|
declare const version: string;
|
|
207
250
|
|
|
208
|
-
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 };
|
|
251
|
+
export { API_BASE_URLS, type Bank, type CardFieldLabels, type CardFieldsOptions, type Crosspay, type CrosspayEnvironment, CrosspayError, type CrosspayOptions, type DeferredCardFields, type DeferredCardFieldsOptions, type DeferredPayInput, FIELDS_URLS, type MountedCardFields, type PaymentResult, type PaymentStatus, type PsePayInput, type SessionSummary, init, version };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var
|
|
1
|
+
var a=class extends Error{constructor(t,d,s){super(t);this.code=d;this.details=s;this.name="CrosspayError"}};var w="crosspay-fields/2";function O(e,r){return typeof e=="object"&&e!==null&&e.protocol===w&&e.channel===r&&typeof e.kind=="string"}function F(e){return new URLSearchParams({channel:e.channel,origin:e.parentOrigin}).toString()}var j=3e4,N={position:"fixed",inset:"0",width:"100%",height:"100%",zIndex:"2147483647"};function v(e,r,t){let d=typeof t.container=="string"?document.querySelector(t.container):t.container;if(!d)return Promise.reject(new a(`No existe el contenedor ${String(t.container)}.`,"INVALID_CONTAINER"));let s=new URL(e).origin,i=T(),o=document.createElement("div");o.style.position="relative";let n=document.createElement("iframe");n.src=`${e}#${F({channel:i,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"}),o.appendChild(n),d.replaceChildren(o);let c=0,p=new Map,m=u=>{n.contentWindow?.postMessage({...u,protocol:w,channel:i},s)},f=u=>{u?(o.style.height=`${c}px`,Object.assign(n.style,N)):(o.style.height="",Object.assign(n.style,{position:"",inset:"",width:"100%",height:`${c}px`,zIndex:""}))},h=!1,E=()=>{if(!h){h=!0,window.removeEventListener("message",I);for(let u of p.values())u.reject(new a("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));p.clear(),o.remove()}},y=null;function I(u){if(u.source!==n.contentWindow||u.origin!==s||!O(u.data,i))return;let l=u.data;switch(l.kind){case"ready":m({kind:"init",options:{publishableKey:r,clientSecret:t.clientSecret,amount:t.amount,currency:t.currency,installments:t.installments,labels:t.labels,styles:t.styles}});break;case"mounted":y?.({session:l.session});break;case"mount-error":y?.(new a(l.message,l.code));break;case"resize":c=Math.max(0,Math.ceil(l.height)),n.style.position!=="fixed"?n.style.height=`${c}px`:o.style.height=`${c}px`;break;case"validity":t.onValidityChange?.(l.valid);break;case"overlay":f(l.active);break;case"paid":{let g=p.get(l.requestId);p.delete(l.requestId),g?.resolve(l.result);break}case"pay-error":{let g=p.get(l.requestId);p.delete(l.requestId),g?.reject(new a(l.message,l.code));break}}}return window.addEventListener("message",I),new Promise((u,l)=>{let g=setTimeout(()=>{y?.(new a("No se pudo cargar el formulario de tarjeta.","FIELDS_TIMEOUT"))},j);y=S=>{if(y=null,clearTimeout(g),S instanceof a){E(),l(S);return}u({session:S.session,pay(P){if(h)return Promise.reject(new a("El formulario de tarjeta se retiro.","FIELDS_DESTROYED"));let x=T();return new Promise((A,$)=>{p.set(x,{resolve:A,reject:$}),m({kind:"pay",requestId:x,...P?{clientSecret:P}:{}})})},destroy:E})}})}function T(){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 R(e){let r=k[e];if(!r)throw new TypeError(`Ambiente de Crosspay no valido: "${String(e)}". Usa "development" o "production".`);return r}var C={development:"https://fields-dev.crosspaysolutions.financial/",production:"https://fields.crosspaysolutions.financial/"};function D(e){let r=(s,i)=>(e.fetch??globalThis.fetch)(s,i);async function t(s,i,o){let n={Accept:"application/json"};o!==void 0&&(n["Content-Type"]="application/json"),e.locale&&(n["Accept-Language"]=e.locale);let c;try{c=await r(`${e.baseUrl}${i}`,{method:s,headers:n,body:o===void 0?void 0:JSON.stringify(o),credentials:"omit"})}catch(m){throw new a("No se pudo conectar con Crosspay.","NETWORK_ERROR",m)}let p=await c.json().catch(()=>null);if(!c.ok||p?.success===!1){let m=p?.message||p?.error||`Crosspay respondio ${c.status}.`;throw new a(m,`HTTP_${c.status}`,p)}return p}async function d(s,i,o){let n=await t(s,i,o);return n&&typeof n=="object"&&"data"in n?n.data:n}return{get:s=>d("GET",s),post:(s,i)=>d("POST",s,i),postRaw:(s,i)=>t("POST",s,i)}}var U="cp_device_id";function B(){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 q(){return typeof navigator>"u"?"":[navigator.userAgent,navigator.language,typeof screen<"u"?`${screen.width}x${screen.height}`:"",b()].join("|")}function H(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 b(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||""}catch{return""}}function L(){let e=B(),r=H(q());return e?`${e}.${r}`:r}function z(){let e=typeof navigator<"u"?navigator.language?.split("-")[0]:void 0;return{fingerprint:L(),timezone:b(),...e?{language:e}:{}}}function _(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 i=String(s.code??"").trim(),o=String(s.name??"").normalize("NFD").replace(/\p{Diacritic}/gu,"").toLowerCase();return i!==""&&i!=="0"&&!o.includes("seleccione su banco")})},async pay(t){let{clientSecret:d,...s}=t,i=await e.postRaw("/payments/sessions/pay",{publishableKey:r,clientSecret:d,parentOrigin:window.location.origin,method:"pse",pse:s,meta:z()}),{data:o,...n}=i??{},c=o?.sessionId;return{...n,...c?{sessionId:c}:{}}}}}var V={development:"pk_test_",production:"pk_live_"};function K(e){let r=R(e.environment),t=String(e.publishableKey??"").trim(),d=V[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??C[e.environment],i=D({baseUrl:r,locale:e.locale,fetch:e.fetch});return{environment:e.environment,card:{async mount(o){if(!String(o.clientSecret??"").trim())throw new a("Falta `clientSecret`: crea la sesion de pago en tu servidor con @crosspay-lb/node.","INVALID_REQUEST");let n=await v(s,t,o);return{session:n.session,pay:()=>n.pay(),destroy:n.destroy}},async mountDeferred(o){let{amount:n,currency:c}=Y(o),p=await v(s,t,{...o,amount:n,currency:c});return{amount:n,currency:c,pay(m){let f=String(m?.clientSecret??"").trim();return f?p.pay(f):Promise.reject(new a("Falta `clientSecret`: crea la sesion de pago en tu servidor antes de pagar.","INVALID_REQUEST"))},destroy:p.destroy}}},payments:_(i),pse:M(i,t)}}function Y(e){if("clientSecret"in e)throw new a("`mountDeferred` no recibe `clientSecret`: pasalo en `pay()`, o usa `mount` si ya tienes la sesion.","INVALID_REQUEST");let r=Number(e.amount);if(!Number.isFinite(r)||r<=0)throw new a("`amount` debe ser un numero mayor que cero.","INVALID_REQUEST");let t=String(e.currency??"").trim().toUpperCase();if(!/^[A-Z]{3}$/.test(t))throw new a('`currency` debe ser un codigo ISO 4217, p. ej. "COP".',"INVALID_REQUEST");return{amount:r,currency:t}}var de="0.4.0";export{k as API_BASE_URLS,a as CrosspayError,C as FIELDS_URLS,K as init,de as version};
|
package/package.json
CHANGED