diff --git a/package-lock.json b/package-lock.json index 91b8d0a..60b8c16 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tonder.io/ionic-lite-sdk", - "version": "0.0.34-beta.1", + "version": "0.0.34-beta.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tonder.io/ionic-lite-sdk", - "version": "0.0.34-beta.1", + "version": "0.0.34-beta.3", "license": "ISC", "dependencies": { "skyflow-js": "^1.34.1", diff --git a/package.json b/package.json index f0ea5a2..82ea8e9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tonder.io/ionic-lite-sdk", - "version": "0.0.34-beta.1", + "version": "0.0.34-beta.3", "description": "Tonder ionic lite SDK", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/src/classes/liteCheckout.ts b/src/classes/liteCheckout.ts index 75c3abd..7b91388 100644 --- a/src/classes/liteCheckout.ts +++ b/src/classes/liteCheckout.ts @@ -1,3 +1,7 @@ +import {injectMercadoPagoSecurity} from "../helpers/mercadopago"; + +declare const MP_DEVICE_SESSION_ID: string | undefined; + import Skyflow from "skyflow-js"; import CollectContainer from "skyflow-js/types/core/external/collect/collect-container"; import CollectElement from "skyflow-js/types/core/external/collect/collect-element"; @@ -41,7 +45,15 @@ export class LiteCheckout implements LiteCheckoutConstructor { apiKey: this.apiKeyTonder, baseUrl: this.baseUrlTonder, }) + this.#init() + } + + async #init(){ this.getActiveAPMs() + const { mercado_pago } = await this.#fetchMerchantData() as Business + if (!!mercado_pago && mercado_pago.active){ + injectMercadoPagoSecurity() + } } async getOpenpayDeviceSessionID( @@ -63,7 +75,9 @@ export class LiteCheckout implements LiteCheckoutConstructor { } async #fetchMerchantData() { try { - this.merchantData = await this.getBusiness(); + if (!this.merchantData){ + this.merchantData = await this.getBusiness(); + } return this.merchantData }catch(e){ return this.merchantData @@ -94,28 +108,37 @@ export class LiteCheckout implements LiteCheckoutConstructor { const result3ds = await this.process3ds.verifyTransactionStatus() const resultCheckout = await this.resumeCheckout(result3ds) this.process3ds.setPayload(resultCheckout) - if (resultCheckout && 'is_route_finished' in resultCheckout && 'provider' in resultCheckout && resultCheckout.provider === 'tonder') { - return resultCheckout - } return this.handle3dsRedirect(resultCheckout) } async resumeCheckout(response: any) { - if (["Failed", "Declined", "Cancelled"].includes(response?.status)) { + // Stop the routing process if the transaction is either hard declined or successful + if (response?.decline?.error_type === "Hard") { + return response + } + + if (["Success", "Authorized"].includes(response?.transaction_status)) { + return response; + } + + if (response) { const routerItems = { - checkout_id: response.checkout?.id, + checkout_id: response?.checkout?.id, }; - const routerResponse = await this.handleCheckoutRouter( - routerItems - ); - return routerResponse + try { + const routerResponse = await this.handleCheckoutRouter( + routerItems + ); + return routerResponse + }catch (error){ + // throw error + } + return response } - return response } async handle3dsRedirect(response: ErrorResponse | StartCheckoutResponse | false | undefined) { const iframe = response && 'next_action' in response ? response?.next_action?.iframe_resources?.iframe:null - if (iframe) { this.process3ds.loadIframe()!.then(() => { //TODO: Check if this will be necessary on the frontend side @@ -206,7 +229,7 @@ export class LiteCheckout implements LiteCheckoutConstructor { "Content-Type": "application/json", Authorization: `Token ${this.apiKeyTonder}`, }, - body: JSON.stringify(data), + body: JSON.stringify({...data, ...(typeof MP_DEVICE_SESSION_ID !== "undefined" ? {mp_device_session_id: MP_DEVICE_SESSION_ID}:{})}), }); if (response.ok) return await response.json() as StartCheckoutResponse; throw await buildErrorResponse(response); @@ -312,7 +335,8 @@ export class LiteCheckout implements LiteCheckoutConstructor { ...( !!payment_method ? {payment_method} : {card: skyflowTokens} - ) + ), + ...(typeof MP_DEVICE_SESSION_ID !== "undefined" ? {mp_device_session_id: MP_DEVICE_SESSION_ID}:{}) }; const checkoutResult = await this.handleCheckoutRouter(routerItems); @@ -432,9 +456,7 @@ export class LiteCheckout implements LiteCheckoutConstructor { async registerCustomerCard(customerToken: string, data: RegisterCustomerCardRequest): Promise { try { - if(!this.merchantData){ - await this.#fetchMerchantData() - } + await this.#fetchMerchantData() const response = await fetch(`${this.baseUrlTonder}/api/v1/business/${getBusinessId(this.merchantData)}/cards/`, { method: 'POST', @@ -455,9 +477,7 @@ export class LiteCheckout implements LiteCheckoutConstructor { async getCustomerCards(customerToken: string): Promise { try { - if(!this.merchantData){ - await this.#fetchMerchantData() - } + await this.#fetchMerchantData() const response = await fetch(`${this.baseUrlTonder}/api/v1/business/${getBusinessId(this.merchantData)}/cards`, { method: 'GET', @@ -477,10 +497,7 @@ export class LiteCheckout implements LiteCheckoutConstructor { async deleteCustomerCard(customerToken: string, skyflowId: string = ""): Promise { try { - if(!this.merchantData){ - await this.#fetchMerchantData() - } - + await this.#fetchMerchantData() const response = await fetch(`${this.baseUrlTonder}/api/v1/business/${getBusinessId(this.merchantData)}/cards/${skyflowId}`, { method: 'DELETE', headers: { diff --git a/src/helpers/mercadopago.ts b/src/helpers/mercadopago.ts new file mode 100644 index 0000000..34e6936 --- /dev/null +++ b/src/helpers/mercadopago.ts @@ -0,0 +1,16 @@ +export function injectMercadoPagoSecurity() { + try { + const script = document.createElement('script'); + script.src = "https://www.mercadopago.com/v2/security.js"; + script.setAttribute('view', ''); + script.onload = () => { + console.log("Mercado Pago script loaded successfully."); + }; + script.onerror = (error) => { + console.error("Error loading Mercado Pago script:", error); + }; + document.head.appendChild(script); + } catch (error) { + console.error("Error attempting to inject Mercado Pago script:", error); + } +} \ No newline at end of file diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index 47ac733..b053b96 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -35,7 +35,6 @@ const buildErrorResponse = async ( response: Response, stack: string | undefined = undefined ): Promise => { - let body, status, message = "Error"; if (response && "json" in response) { @@ -46,10 +45,13 @@ const buildErrorResponse = async ( status = response.status.toString(); } - if (response && "text" in response) { + if (!body && response && "text" in response) { message = await response.text(); } + if(body?.detail){ + message = body.detail + } const error = new ErrorResponse({ code: status, body: body, diff --git a/src/types/commons.ts b/src/types/commons.ts index 23e8e1e..e1bee9a 100644 --- a/src/types/commons.ts +++ b/src/types/commons.ts @@ -23,6 +23,9 @@ export type Business = { fintoc_keys: { public_key: string; }; + mercado_pago: { + active: boolean; + }; vault_id: string; vault_url: string; reference: number; diff --git a/tests/utils/mockClasses.ts b/tests/utils/mockClasses.ts index df8315a..0e355bb 100644 --- a/tests/utils/mockClasses.ts +++ b/tests/utils/mockClasses.ts @@ -39,6 +39,7 @@ export class BusinessClass implements Business { vault_url!: string; reference!: number; is_installments_available!: boolean; + mercado_pago!: { active: boolean} get mockObject(): GetBusinessResponse { return { @@ -59,6 +60,9 @@ export class BusinessClass implements Business { textDetailsColor: '#666666', checkout_logo: 'assets/images/checkout-logo.png', }, + mercado_pago: { + active: false + }, vault_id: 'mock-vault-id-123', vault_url: 'https://mock-vault.com', reference: 987654,