diff --git a/.gitignore b/.gitignore index 5d4a1d0..a075919 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,4 @@ lib .env /dist -/types \ No newline at end of file +.idea/ diff --git a/README.md b/README.md index 8dbd6e4..ed3e1b0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,22 @@ # Tonder SDK -Tonder SDK Lite to integrate REST service +Tonder SDK helps to integrate the services Tonder offers in your own mobile app + + +## Table of Contents + +1. [Installation](#installation) +2. [Usage](#usage) +3. [Configuration Options](#configuration-options) +4. [Mobile Settings](#mobile-settings) +5. [Payment Data Structure](#payment-data-structure) +6. [Field Validation Functions](#field-validation-functions) +7. [API Reference](#api-reference) +8. [Examples](#examples) +9. [Deprecated Fields](#deprecated-fields) +10. [Deprecated Functions](#deprecated-functions) +11. [License](#license) + ## Installation @@ -16,11 +32,12 @@ Add dependencies to the root of the app (index.html) ``` ## Usage -## Import LiteCheckout class +LiteCheckout allows you to build a custom checkout interface using Tonder's core functionality +### Import LiteCheckout class ```javascript import { LiteCheckout } from "@tonder.io/ionic-lite-sdk" ``` -## Create instance +### Create instance ```javascript const liteCheckout = new LiteCheckout({ @@ -29,104 +46,391 @@ const liteCheckout = new LiteCheckout({ publicApiKeyTonder }) +// The configureCheckout function allows you to set initial information, +// such as the customer's email, which is used to retrieve a list of saved cards, save new card, etc. +inlineCheckout.configureCheckout({ customer: { email: "example@email.com" } }); + +// Initialize the checkout +await liteCheckout.injectCheckout(); + // To verify a 3ds transaction you can use the following method // It should be called after the injectCheckout method // The response status will be one of the following // ['Declined', 'Cancelled', 'Failed', 'Success', 'Pending', 'Authorized'] inlineCheckout.verify3dsTransaction().then(response => { - console.log('Verify 3ds response', response) + console.log('Verify 3ds response', response) }) ``` -| Property | Type | Description | -|:---------------:|:-------------:|:-----------------------------------------------------------------------:| -| signal | AborSignal | Signal from AbortController instance if it need cancel request | -| baseUrlTonder | string | Live server: http://stage.tonder.io | -| | | Mock Server: https://stoplight.io/mocks/tonder/tonder-api-v1-2/3152148 | -| publicApiKeyTonder | string | You can take this from you Tonder Dashboard | -| | | | +```javascript +// Retrieve customer's saved cards +const cards = await liteCheckout.getCustomerCards(); +``` -# Class methods +```javascript +// Save a new card +const newCard = await liteCheckout.saveCustomerCard(cardData); +``` -# Checkout router +```javascript +// Remove a saved card +await liteCheckout.removeCustomerCard(cardId); +``` -```typescript +```javascript +// Get available payment methods +const paymentMethods = await liteCheckout.getCustomerPaymentMethods(); +``` + +```javascript +// Process a payment +const paymentResponse = await liteCheckout.payment(paymentData); +``` + +## Configuration Options + +| Property | Type | Description | +|:---------:|:--------:|:--------------------------------------------------------------------------------------------:| +| mode | string | Environment mode. Options: 'stage', 'production', 'sandbox', 'development'. Default: 'stage' | +| publicApiKey | string | Your API key from the Tonder Dashboard | +| returnrl | string | URL where the checkout form is mounted (used for 3DS) | +| callBack | function | Callback function to be invoked after the payment process ends successfully. | + +## Mobile settings + +If you are deploying to Android, edit your AndroidManifest.xml file to add the Internet permission. + +```xml + + +``` + +Likewise, if you are deploying to macOS, edit your macos/Runner/DebugProfile.entitlements and macos/Runner/Release.entitlements files to include the network client entitlement. + +```xml + +com.apple.security.network.client + +``` + +## Payment Data Structure + +When calling the `payment` method, use the following data structure: -const returnUrl = "http://localhost:8100/payment/success"; +### Field Descriptions -let checkoutData = { +- **customer**: Object containing the customer's personal information to be registered in the transaction. + +- **cart**: Object containing the total amount and an array of items to be registered in the Tonder order. + + - **total**: The total amount of the transaction. + - **items**: An array of objects, each representing a product or service in the order. + - name: name of the product + - price_unit: valid float string with the price of the product + - quantity: valid integer string with the quantity of this product + +- **currency**: String representing the currency code for the transaction (e.g., "MXN" for Mexican Peso). + +- **metadata**: Object for including any additional information about the transaction. This can be used for internal references or tracking. + +- **card**: (for LiteCheckout) Object containing card information. This is used differently depending on whether it's a new card or a saved card: + + - For a new card: Include `card_number`, `cvv`, `expiration_month`, `expiration_year`, and `cardholder_name`. + - For a saved card: Include only the `skyflow_id` of the saved card. + - This is only used when not paying with a payment_method. + +- **payment_method**: (for LiteCheckout) String indicating the alternative payment method to be used (e.g., "Spei"). This is only used when not paying with a card. + +```javascript +const paymentData = { customer: { - name: "Jhon", - lastname: "Doe", - email: "john.c.calhoun@examplepetstore.com", - phone: "+58452258525" + firstName: "John", + lastName: "Doe", + country: "USA", + address: "123 Main St", + city: "Anytown", + state: "CA", + postCode: "12345", + email: "john.doe@example.com", + phone: "1234567890", }, - order: { + cart: { + total: "100.00", items: [ { - description: "Test product description", + description: "Product description", quantity: 1, - price_unit: 25, - discount: 1, - taxes: 12, - product_reference: 89456123, - name: "Test product", - amount_total: 25 - } - ] + price_unit: "100.00", + discount: "0.00", + taxes: "0.00", + product_reference: "PROD123", + name: "Product Name", + amount_total: "100.00", + }, + ], }, - return_url: returnUrl, - total: 25, - isSandbox: true, - metadata: {}, currency: "MXN", - skyflowTokens: { - cardholder_name: "", - card_number: "", - expiration_year: "", - expiration_month: "", - cvv: "", - skyflow_id: "" - } -} + metadata: { + order_id: "ORDER123", + }, + // For a new card: + card: { + card_number: "4111111111111111", + cvv: "123", + expiration_month: "12", + expiration_year: "25", + cardholder_name: "John Doe", + }, + // card: "skyflow_id" // for a selected saved card. + // payment_method: "Spei", // For the selected payment method. +}; +``` + +## Field Validation Functions + +For LiteCheckout implementations, the SDK provides validation functions to ensure the integrity of card data before submitting: + +- `validateCardNumber(cardNumber)`: Validates the card number using the Luhn algorithm. +- `validateCardholderName(name)`: Checks if the cardholder name is valid. +- `validateCVV(cvv)`: Ensures the CVV is in the correct format. +- `validateExpirationDate(expirationDate)`: Validates the expiration date in MM/YY format. +- `validateExpirationMonth(month)`: Checks if the expiration month is valid. +- `validateExpirationYear(year)`: Validates the expiration year. +Example usage: + +```javascript +import { + validateCardNumber, + validateCardholderName, + validateCVV, + validateExpirationDate, +} from "@tonder.io/ionic-lite-sdk"; + +const cardNumber = "4111111111111111"; +const cardholderName = "John Doe"; +const cvv = "123"; +const expirationDate = "12/25"; + +if ( + validateCardNumber(cardNumber) && + validateCardholderName(cardholderName) && + validateCVV(cvv) && + validateExpirationDate(expirationDate) +) { + // Proceed with payment +} else { + // Show error message +} ``` -It is required get the skyflow tokens to add it to the checkout router method, the values of the variable skyflowFields come from your html form + +## API Reference + +### LiteCheckout Methods + +- `configureCheckout(data)`: Set initial checkout data +- `injectCheckout()`: Initialize the checkout +- `getCustomerCards()`: Retrieve saved cards +- `saveCustomerCard(cardData)`: Save a new card +- `removeCustomerCard(cardId)`: Remove a saved card +- `getCustomerPaymentMethods()`: Get available payment methods +- `payment(data)`: Process a payment +- `verify3dsTransaction()`: Verify a 3DS transaction + + +## Examples + +Here are examples of how to implement Tonder Lite SDK: + +### Angular + +For Angular, we recommend using a service to manage the Tonder instance: ```typescript +// tonder.service.ts +import { Injectable } from "@angular/core"; +import { LiteCheckout } from "@tonder.io/ionic-lite-sdk"; +import {ILiteCheckout} from "@tonder.io/ionic-lite-sdk/dist/types/liteInlineCheckout"; + +@Injectable({ + providedIn: "root", +}) +export class TonderService { + private liteCheckout!: ILiteCheckout; + + constructor(@Inject(Object) private sdkParameters: IInlineLiteCheckoutOptions) { + this.initializeInlineCheckout(); + } -const merchantData: any = await liteCheckout.getBusiness(); + private initializeInlineCheckout(): void { + this.liteCheckout = new LiteCheckout({ ...this.sdkParameters }); + } -const { vault_id, vault_url } = merchantData; + configureCheckout(customerData: IConfigureCheckout): void { + return this.liteCheckout.configureCheckout({ ...customerData }); + } -const skyflowFields = { - card_number: this.paymentForm.value.cardNumber, - cvv: this.paymentForm.value.cvv, - expiration_month: this.paymentForm.value.month, - expiration_year: this.paymentForm.value.expirationYear, - cardholder_name: this.paymentForm.value.name + async injectCheckout(): Promise { + return await this.liteCheckout.injectCheckout(); + } + + verify3dsTransaction(): Promise { + return this.liteCheckout.verify3dsTransaction(); + } + + payment( + checkoutData: IProcessPaymentRequest, + ): Promise { + return this.inlineCheckout.payment(checkoutData); + } + + // Add more functions, for example for lite sdk: get payment methods + + // getCustomerPaymentMethods(): Promise { + // return this.liteCheckout.getCustomerPaymentMethods(); + // } } -const skyflowTokens = await liteCheckout.getSkyflowTokens({ - vault_id: vault_id, - vault_url: vault_url, - data: skyflowFields +// checkout.component.ts +import { Component, OnInit, OnDestroy } from "@angular/core"; +import { TonderService } from "./tonder.service"; + +@Component({ + selector: "app-tonder-checkout", + template: ` +
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
{{ errorMessage }}
+
+
+ +
+
+ +
+
+ `, + providers: [ + { + provide: TonderInlineService, + // Initialization of the Tonder Lite SDK. + // Note: Replace these credentials with your own in development/production. + useFactory: () => + new TonderInlineService({ + apiKey: "11e3d3c3e95e0eaabbcae61ebad34ee5f93c3d27", + returnUrl: "http://localhost:8100/tabs/tab5", + mode: "stage", + }), + }, + ], }) +export class TonderCheckoutComponent implements OnInit, OnDestroy { + loading = false; + checkoutData: IProcessPaymentRequest; + paymentForm = new FormGroup({ + name: new FormControl('Pedro Paramo'), + cardNumber: new FormControl('4242424242424242'), + month: new FormControl('12'), + expirationYear: new FormControl('28'), + cvv: new FormControl('123') + }); + + constructor(private tonderService: TonderService) { + this.checkoutData = { + customer: { + firstName: "Jhon", + lastName: "Doe", + email: "john.c.calhoun@examplepetstore.com", + phone: "+58452258525" + }, + cart: { + total: 25, + items: [ + { + description: "Test product description", + quantity: 1, + price_unit: 25, + discount: 1, + taxes: 12, + product_reference: 89456123, + name: "Test product", + amount_total: 25 + } + ] + }, + metadata: {}, + currency: "MXN" + } + } -checkoutData.skyflowTokens = skyflowTokens; + ngOnInit() { + this.initCheckout(); + } -const jsonResponseRouter: any = await liteCheckout.startCheckoutRouterFull( - checkoutData -); + async initCheckout() { + this.tonderService.configureCheckout({ + customer: { email: "example@email.com" }, + }); + await this.tonderService.injectCheckout(); + this.tonderService.verify3dsTransaction().then((response) => { + console.log("Verify 3ds response", response); + }); + + // Calls more functions to get payment methods, saved cards, etc. + } + async pay() { + this.loading = true; + try { + const response = await this.tonderService.payment({ + ...this.checkoutData, + card: { // Card details, if not using a payment method. + card_number: this.paymentForm.value.cardNumber || "", + cvv: this.paymentForm.value.cvv || "", + expiration_month: this.paymentForm.value.month || "", + expiration_year: this.paymentForm.value.expirationYear || "", + cardholder_name: this.paymentForm.value.name || "" + }, + // card: "skyflow_id" // In case a saved card is selected. + // payment_method: "" // Payment method if not using the card form + }); + console.log("Payment successful:", response); + alert("Payment successful"); + } catch (error) { + console.error("Payment failed:", error); + alert("Payment failed"); + } finally { + this.loading = false; + } + } +} ``` -Take actions on base to the checkout router response - -# Customer Cards(Register) - ## Request secure token ```typescript @@ -145,75 +449,82 @@ const jsonResponse = await liteCheckout.getSecureToken( } ``` +## Deprecated Fields + +The following fields have been deprecated and should no longer be used. Consider using the recommended alternatives: + ## Register customer card +### `apiKeyTonder` Property -```typescript +- **Deprecated Reason:** The `apiKeyTonder` property in the constructor and `IInlineLiteCheckoutOptions` interface is no longer required. +- **Alternative:** Use the `apiKey` field. -customer_auth_token: string; +### `baseUrlTonder` Property -data: { - skyflow_id: string; -}; +- **Deprecated Reason:** The `baseUrlTonder` property in the constructor and `IInlineLiteCheckoutOptions` interface is no longer required. +- **Alternative:** Use the `mode` field with `stage` | `development` | `sandbox` | `production` options. -const jsonResponseOrder = await liteCheckout.registerCustomerCard( - secureToken - customer_auth_token, - data -); -``` +### `signal` Property -## Return register customer card -```typescript -{ - skyflow_id: string; - user_id: number; -} -``` +- **Deprecated Reason:** The `signal` property in the constructor and `IInlineLiteCheckoutOptions` interface is no longer required. -# Customer Cards(Get) -## Get customer cards +## Deprecated Functions -```typescript +The following functions have been deprecated and should no longer be used. Consider using the recommended alternatives: -customer_auth_token: string; +### `customerRegister` -query: string = "?ordering=&search="; +- **Deprecated Reason:** This function is no longer necessary as registration is now automatically handled during payment processing or when using card management methods. -const jsonResponseOrder = await liteCheckout.getCustomerCards( - customer_auth_token, - query -); -``` +### `createOrder` and `createPayment` -## Return get customer cards -```typescript -{ - user_id: number, - cards: [ - { - fields: { - card_number: string, - cardholder_name: string, - cvv: string, - expiration_month: string, - expiration_year: string, - skyflow_id: string - } - } - ] -} -``` +- **Deprecated Reason:** These functions have been replaced by the `payment` function, which now automatically handles order creation and payment processing. +- **Alternative:** Use the `payment` function. -## Delete customer card +### `startCheckoutRouter` and `startCheckoutRouterFull` -```typescript +- **Deprecated Reason:** These functions have been replaced by the `payment` function. +- **Alternative:** Use the `payment` function. -const deleted: boolean = await liteCheckout.deleteCustomerCard( - customer_auth_token, - skyflow_id -); +### `registerCustomerCard` + +- **Deprecated Reason:** This function has been renamed to `saveCustomerCard` to better align with its purpose. The method's usage has also been updated. +- **Alternative:** Use the `saveCustomerCard` method and update your implementation to reflect the changes. + +### `deleteCustomerCard` +- **Deprecated Reason:** This function has been renamed to `removeCustomerCard` to better align with its purpose. The method's usage has also been updated. +- **Alternative:** Use the `removeCustomerCard` method and update your implementation to reflect the changes. + +### `getActiveAPMs` + +- **Deprecated Reason:** This function has been renamed to `getCustomerPaymentMethods` to better align with its purpose. The method's usage has also been updated. +- **Alternative:** Use the `getCustomerPaymentMethods` method and update your implementation to reflect the changes. + +### `getSkyflowTokens` + +- **Deprecated Reason:** Card registration and checkout are now automatically handled during the payment process or through card management methods, making this method unnecessary. + +### `getOpenpayDeviceSessionID` + +- **Deprecated Reason:** It is no longer necessary to use this method is now automatically handled during the payment process. + + +## Notes + +### General + +- Replace `apiKey`, `mode`, `returnUrl` with your actual values. +- Remember to use the `configureCheckout` function after creating an instance of `LiteCheckout`. This ensures that functions such as payment processing, saving cards, deleting cards, and others work correctly. + +### Script Dependencies + +For all implementations, ensure you include the necessary scripts: + +```html + + ``` ## License diff --git a/package-lock.json b/package-lock.json index 001eb7e..05cde97 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,15 @@ { "name": "@tonder.io/ionic-lite-sdk", - "version": "0.0.36-beta.1", + "version": "0.0.40-beta.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tonder.io/ionic-lite-sdk", - "version": "0.0.36-beta.1", + "version": "0.0.40-beta.1", "license": "ISC", "dependencies": { + "lodash.get": "^4.4.2", "skyflow-js": "^1.34.1", "ts-node": "^10.9.2" }, @@ -17,6 +18,8 @@ "@rollup/plugin-typescript": "11.1.6", "@types/crypto-js": "^4.2.2", "@types/jest": "^29.5.11", + "@types/lodash": "^4.17.10", + "@types/lodash.get": "^4.4.9", "@types/node": "^20.11.5", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -1595,6 +1598,21 @@ "dev": true, "peer": true }, + "node_modules/@types/lodash": { + "version": "4.17.10", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.10.tgz", + "integrity": "sha512-YpS0zzoduEhuOWjAotS6A5AVCva7X4lVlYLF0FYHAY9sdraBfnatttHItlWeZdGhuEkf+OzMNg2ZYAx8t+52uQ==", + "dev": true + }, + "node_modules/@types/lodash.get": { + "version": "4.4.9", + "resolved": "https://registry.npmjs.org/@types/lodash.get/-/lodash.get-4.4.9.tgz", + "integrity": "sha512-J5dvW98sxmGnamqf+/aLP87PYXyrha9xIgc2ZlHl6OHMFR2Ejdxep50QfU0abO1+CH6+ugx+8wEUN1toImAinA==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/node": { "version": "20.11.16", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz", @@ -4720,6 +4738,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", diff --git a/package.json b/package.json index 8bf1258..b4489b0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@tonder.io/ionic-lite-sdk", - "version": "0.0.36-beta.1", + "version": "0.0.40-beta.1", "description": "Tonder ionic lite SDK", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -12,6 +12,7 @@ "author": "", "license": "ISC", "dependencies": { + "lodash.get": "^4.4.2", "skyflow-js": "^1.34.1", "ts-node": "^10.9.2" }, @@ -24,6 +25,8 @@ "@rollup/plugin-typescript": "11.1.6", "@types/crypto-js": "^4.2.2", "@types/jest": "^29.5.11", + "@types/lodash": "^4.17.10", + "@types/lodash.get": "^4.4.9", "@types/node": "^20.11.5", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", diff --git a/src/classes/BaseInlineCheckout.ts b/src/classes/BaseInlineCheckout.ts new file mode 100644 index 0000000..638c0b8 --- /dev/null +++ b/src/classes/BaseInlineCheckout.ts @@ -0,0 +1,392 @@ +import { ThreeDSHandler } from "./3dsHandler"; +import { ErrorResponse } from "./errorResponse"; +import { fetchBusiness } from "../data/businessApi"; +import { injectMercadoPagoSecurity } from "../helpers/mercadopago"; +import { TONDER_URL_BY_MODE } from "../shared/constants/tonderUrl"; +import { + createOrder, + createPayment, + startCheckoutRouter, +} from "../data/checkoutApi"; +import { getOpenpayDeviceSessionID } from "../data/openPayApi"; +import { getBrowserInfo } from "../helpers/utils"; +import { registerOrFetchCustomer } from "../data/customerApi"; +import { get } from "lodash"; +import { + fetchCustomerCards, + removeCustomerCard, + saveCustomerCard, +} from "../data/cardApi"; +import { fetchCustomerPaymentMethods } from "../data/paymentMethodApi"; +import {Business, IConfigureCheckout, IInlineCheckoutBaseOptions} from "../types/commons"; +import {ICustomer} from "../types/customer"; +import {ICardFields, IItem, IProcessPaymentRequest, IStartCheckoutResponse} from "../types/checkout"; +import {ICustomerCardsResponse, ISaveCardResponse, ISaveCardSkyflowRequest} from "../types/card"; +import {IPaymentMethodResponse} from "../types/paymentMethod"; +import {ITransaction} from "../types/transaction"; +export class BaseInlineCheckout { + baseUrl = ""; + cartTotal: string | number = "0"; + process3ds: ThreeDSHandler; + mode?: "production" | "sandbox" | "stage" | "development" | undefined; + apiKeyTonder: string; + returnUrl?: string; + callBack?: ((response: IStartCheckoutResponse | Record) => void) | undefined; + merchantData?: Business; + abortController: AbortController; + secureToken: string = ""; + customer?: ICustomer | { email: string }; + + cartItems?: IItem[]; + metadata = {}; + card? = {}; + currency?: string = ""; + + #customerData?: Record; + + constructor({ + mode = "stage", + apiKey, + apiKeyTonder, + returnUrl, + callBack = () => {}, + baseUrlTonder + }: IInlineCheckoutBaseOptions) { + this.apiKeyTonder = apiKeyTonder || apiKey || ""; + this.returnUrl = returnUrl; + this.callBack = callBack; + this.mode = mode; + this.customer = {} as ICustomer + this.baseUrl = baseUrlTonder || TONDER_URL_BY_MODE[this.mode] || TONDER_URL_BY_MODE["stage"]; + this.abortController = new AbortController(); + this.process3ds = new ThreeDSHandler({ + apiKey: apiKey, + baseUrl: this.baseUrl, + }); + } + + configureCheckout(data: IConfigureCheckout) { + if ("customer" in data) this.#handleCustomer(data["customer"]); + if ("secureToken" in data) this.#setSecureToken(data["secureToken"]); + } + + async verify3dsTransaction(): Promise { + const result3ds = await this.process3ds.verifyTransactionStatus(); + const resultCheckout = await this.#resumeCheckout(result3ds); + this.process3ds.setPayload(resultCheckout); + return this._handle3dsRedirect(resultCheckout); + } + + payment(data: IProcessPaymentRequest): Promise { + return new Promise(async (resolve, reject) => { + try { + this.#handleCustomer(data.customer); + this._setCartTotal(data.cart?.total); + this.#setCartItems(data.cart?.items); + this.#handleMetadata(data); + this.#handleCurrency(data); + this.#handleCard(data); + const response = await this._checkout(data); + this.process3ds.setPayload(response); + if (this.callBack) this.callBack!(response); + const payload = await this._handle3dsRedirect(response); + if (payload) { + resolve(response); + } + } catch (error) { + reject(error); + } + }); + } + + async _initializeCheckout() { + const business_response = await this._fetchMerchantData(); + + if ( + !!business_response && + !!business_response.mercado_pago && + business_response.mercado_pago.active + ) { + injectMercadoPagoSecurity(); + } + } + + async _checkout(data: any): Promise { + throw new Error( + "The #checkout method should be implement in child classes.", + ); + } + + _setCartTotal(total: string | number) { + throw new Error( + "The #setCartTotal method should be implement in child classes.", + ); + } + + async _getCustomer(signal: AbortSignal | null = null) { + if (!!this.#customerData) return this.#customerData!; + + this.#customerData = await registerOrFetchCustomer( + this.baseUrl, + this.apiKeyTonder, + this.customer!, + signal, + ); + return this.#customerData!; + } + + async _handleCheckout({ + card, + payment_method, + customer, + isSandbox, + // TODO: DEPRECATED + returnUrl: returnUrlData + }: { + card?: string; + payment_method?: string; + customer: Record; + isSandbox?: boolean; + returnUrl?: string; + }) { + const { openpay_keys, reference, business } = this.merchantData!; + const total = Number(this.cartTotal); + try { + let deviceSessionIdTonder; + if ( + !deviceSessionIdTonder && + openpay_keys.merchant_id && + openpay_keys.public_key + ) { + deviceSessionIdTonder = await getOpenpayDeviceSessionID( + openpay_keys.merchant_id, + openpay_keys.public_key, + isSandbox, + this.abortController.signal, + ); + } + + const { id, auth_token } = customer; + + const orderItems = { + business: this.apiKeyTonder, + client: auth_token, + billing_address_id: null, + shipping_address_id: null, + amount: total, + status: "A", + reference: reference, + is_oneclick: true, + items: this.cartItems!, + }; + const jsonResponseOrder = await createOrder( + this.baseUrl, + this.apiKeyTonder, + orderItems, + ); + + // Create payment + const now = new Date(); + const dateString = now.toISOString(); + + const paymentItems = { + business_pk: business.pk, + client_id: id, + amount: total, + date: dateString, + order_id: jsonResponseOrder.id, + }; + const jsonResponsePayment = await createPayment( + this.baseUrl, + this.apiKeyTonder, + paymentItems, + ); + + // Checkout router + const routerItems = { + name: get(this.customer, "firstName", get(this.customer, "name", "")), + last_name: get( + this.customer, + "lastName", + get(this.customer, "lastname", ""), + ), + email_client: get(this.customer, "email", ""), + phone_number: get(this.customer, "phone", ""), + return_url: returnUrlData || this.returnUrl, + id_product: "no_id", + quantity_product: 1, + id_ship: "0", + instance_id_ship: "0", + amount: total, + title_ship: "shipping", + description: "transaction", + device_session_id: deviceSessionIdTonder ? deviceSessionIdTonder : null, + token_id: "", + order_id: jsonResponseOrder.id, + business_id: business.pk, + payment_id: jsonResponsePayment.pk, + source: "sdk", + metadata: this.metadata, + browser_info: getBrowserInfo(), + currency: this.currency!, + ...(!!payment_method ? { payment_method } : { card }), + }; + + const jsonResponseRouter = await startCheckoutRouter( + this.baseUrl, + this.apiKeyTonder, + routerItems, + ); + + if (jsonResponseRouter) { + return jsonResponseRouter; + } else { + return false; + } + } catch (error) { + console.log(error); + throw error; + } + } + + async _fetchMerchantData() { + try { + if (!this.merchantData) { + this.merchantData = await fetchBusiness( + this.baseUrl, + this.apiKeyTonder, + this.abortController.signal, + ); + } + return this.merchantData; + } catch (e) { + return this.merchantData; + } + } + + async _getCustomerCards( + authToken: string, + businessId: string | number, + ): Promise { + return await fetchCustomerCards(this.baseUrl, authToken, businessId); + } + + async _saveCustomerCard( + authToken: string, + secureToken: string, + businessId: string | number, + skyflowTokens: ISaveCardSkyflowRequest, + ): Promise { + return await saveCustomerCard( + this.baseUrl, + this.secureToken, + authToken, + businessId, + skyflowTokens, + ); + } + + async _removeCustomerCard( + authToken: string, + businessId: string | number, + skyflowId: string, + ): Promise { + return await removeCustomerCard( + this.baseUrl, + authToken, + skyflowId, + businessId, + ); + } + async _fetchCustomerPaymentMethods(): Promise { + return await fetchCustomerPaymentMethods(this.baseUrl, this.apiKeyTonder); + } + + #handleCustomer(customer: ICustomer | { email: string }) { + if (!customer) return; + + this.customer = customer; + } + + #setSecureToken(token: string) { + this.secureToken = token; + } + + #setCartItems(items: IItem[]) { + this.cartItems = items; + } + + #handleMetadata(data: { metadata?: any }) { + this.metadata = data?.metadata; + } + + #handleCurrency(data: { currency?: string }) { + this.currency = data?.currency; + } + + #handleCard(data: { card?: ICardFields | string }) { + this.card = data?.card; + } + + // TODO: Make private after remove deprecated functions of liteCheckout + async _handle3dsRedirect( + response: ITransaction | IStartCheckoutResponse | void, + ) { + 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 + // after some the tests in production, since the 3DS process + // doesn't works properly on the sandbox environment + // setTimeout(() => { + // process3ds.verifyTransactionStatus(); + // }, 10000); + this.process3ds.verifyTransactionStatus(); + }) + .catch((error) => { + console.log("Error loading iframe:", error); + }); + } else { + const redirectUrl = this.process3ds.getRedirectUrl(); + if (redirectUrl) { + this.process3ds.redirectToChallenge(); + } else { + return response; + } + } + } + + async #resumeCheckout(response: any) { + // 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, + }; + + try { + return await startCheckoutRouter( + this.baseUrl, + this.apiKeyTonder, + routerItems, + ); + } catch (error) { + // throw error + } + return response; + } + } +} diff --git a/src/classes/errorResponse.ts b/src/classes/errorResponse.ts index 7d1671b..435c5ab 100644 --- a/src/classes/errorResponse.ts +++ b/src/classes/errorResponse.ts @@ -1,4 +1,4 @@ -import { IErrorResponse } from "../types/responses"; +import {IErrorResponse} from "../types/responses"; export class ErrorResponse implements IErrorResponse { code?: string | undefined; diff --git a/src/classes/liteCheckout.ts b/src/classes/liteCheckout.ts index 68d3245..e99636b 100644 --- a/src/classes/liteCheckout.ts +++ b/src/classes/liteCheckout.ts @@ -1,17 +1,40 @@ -import {injectMercadoPagoSecurity} from "../helpers/mercadopago"; +import { fetchBusiness } from "../data/businessApi"; 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"; -import { APM, Business, TonderAPM } from "../types/commons"; -import { CreateOrderRequest, CreatePaymentRequest, RegisterCustomerCardRequest, StartCheckoutRequest, TokensRequest, StartCheckoutFullRequest, StartCheckoutIdRequest } from "../types/requests"; -import { GetSecureTokenResponse, GetBusinessResponse, CustomerRegisterResponse, CreateOrderResponse, CreatePaymentResponse, StartCheckoutResponse, GetVaultTokenResponse, IErrorResponse, GetCustomerCardsResponse, RegisterCustomerCardResponse } from "../types/responses"; import { ErrorResponse } from "./errorResponse"; -import { buildErrorResponse, buildErrorResponseFromCatch, getBrowserInfo, getPaymentMethodDetails, getBusinessId } from "../helpers/utils"; -import { ThreeDSHandler } from "./3dsHandler"; +import { + buildErrorResponse, + buildErrorResponseFromCatch, + getBrowserInfo, + getBusinessId, + formatPublicErrorResponse, + getCardType, +} from "../helpers/utils"; import { getCustomerAPMs } from "../data/api"; +import { BaseInlineCheckout } from "./BaseInlineCheckout"; +import { MESSAGES } from "../shared/constants/messages"; +import { getSkyflowTokens } from "../helpers/skyflow"; +import { startCheckoutRouter } from "../data/checkoutApi"; +import { getOpenpayDeviceSessionID } from "../data/openPayApi"; +import { getPaymentMethodDetails } from "../shared/catalog/paymentMethodsCatalog"; +import {APM, IInlineLiteCheckoutOptions, TonderAPM} from "../types/commons"; +import {ICustomerCardsResponse, ISaveCardRequest, ISaveCardResponse, ISaveCardSkyflowRequest} from "../types/card"; +import {IPaymentMethod} from "../types/paymentMethod"; +import { + CreateOrderResponse, + CreatePaymentResponse, + CustomerRegisterResponse, + GetBusinessResponse, IErrorResponse, RegisterCustomerCardResponse, StartCheckoutResponse, GetSecureTokenResponse +} from "../types/responses"; +import { + CreateOrderRequest, + CreatePaymentRequest, RegisterCustomerCardRequest, StartCheckoutFullRequest, + StartCheckoutIdRequest, + StartCheckoutRequest, + TokensRequest +} from "../types/requests"; +import {ICardFields, IStartCheckoutResponse} from "../types/checkout"; +import {ILiteCheckout} from "../types/liteInlineCheckout"; declare global { interface Window { @@ -19,256 +42,341 @@ declare global { } } -export type LiteCheckoutConstructor = { - signal: AbortSignal; - baseUrlTonder: string; - publicApiKeyTonder: string; -}; - -export class LiteCheckout implements LiteCheckoutConstructor { - signal: AbortSignal; - baseUrlTonder: string; - publicApiKeyTonder: string; - process3ds: ThreeDSHandler; - activeAPMs: APM[] = [] - merchantData?: Business | ErrorResponse; - - constructor({ - signal, - baseUrlTonder, - publicApiKeyTonder, - }: LiteCheckoutConstructor) { - this.baseUrlTonder = baseUrlTonder; - this.signal = signal; - this.publicApiKeyTonder = publicApiKeyTonder; - this.process3ds = new ThreeDSHandler({ - apiKey: this.publicApiKeyTonder, - baseUrl: this.baseUrlTonder, - }) - this.#init() +export class LiteCheckout extends BaseInlineCheckout implements ILiteCheckout{ + activeAPMs: APM[] = []; + + constructor({ apiKey, mode, returnUrl, callBack, apiKeyTonder, baseUrlTonder }: IInlineLiteCheckoutOptions) { + super({ mode, apiKey, returnUrl, callBack, apiKeyTonder, baseUrlTonder }); } - async #init(){ - this.getActiveAPMs() - const { mercado_pago } = await this.#fetchMerchantData() as Business - if (!!mercado_pago && mercado_pago.active){ - injectMercadoPagoSecurity() - } + public async injectCheckout() { + await this._initializeCheckout(); } - async getOpenpayDeviceSessionID( - merchant_id: string, - public_key: string, - is_sandbox: boolean - ): Promise { + public async getCustomerCards(): Promise { try { - let openpay = await window.OpenPay; - openpay.setId(merchant_id); - openpay.setApiKey(public_key); - openpay.setSandboxMode(is_sandbox); - return await openpay.deviceData.setup({ - signal: this.signal, - }) as string; - } catch (e) { - throw buildErrorResponseFromCatch(e); + await this._fetchMerchantData(); + const { auth_token } = await this._getCustomer(); + const response = await this._getCustomerCards( + auth_token, + this.merchantData!.business.pk, + ); + + return { + ...response, + cards: response.cards.map((ic) => ({ + ...ic, + icon: getCardType(ic.fields.card_scheme), + })), + }; + } catch (error) { + throw formatPublicErrorResponse( + { + message: MESSAGES.getCardsError, + }, + error, + ); } } - async #fetchMerchantData() { + + public async saveCustomerCard( + secureToken: string, + card: ISaveCardRequest, + ): Promise { try { - if (!this.merchantData){ - this.merchantData = await this.getBusiness(); - } - return this.merchantData - }catch(e){ - return this.merchantData + await this._fetchMerchantData(); + const { auth_token } = await this._getCustomer(); + const { vault_id, vault_url, business } = this.merchantData!; + + const skyflowTokens: ISaveCardSkyflowRequest = await getSkyflowTokens({ + vault_id: vault_id, + vault_url: vault_url, + data: card, + baseUrl: this.baseUrl, + apiKey: this.apiKeyTonder, + }); + + return await this._saveCustomerCard( + secureToken, + auth_token, + business?.pk, + skyflowTokens, + ); + } catch (error) { + throw formatPublicErrorResponse( + { + message: MESSAGES.saveCardError, + }, + error, + ); } } - async getBusiness(): Promise { + public async removeCustomerCard(skyflowId: string): Promise { try { - const getBusiness = await fetch( - `${this.baseUrlTonder}/api/v1/payments/business/${this.publicApiKeyTonder}`, + await this._fetchMerchantData(); + const { auth_token } = await this._getCustomer(); + const { business } = this.merchantData!; + + return await this._removeCustomerCard( + auth_token, + business?.pk, + skyflowId, + ); + } catch (error) { + throw formatPublicErrorResponse( { - headers: { - Authorization: `Token ${this.publicApiKeyTonder}`, - }, - signal: this.signal, - } + message: MESSAGES.removeCardError, + }, + error, ); + } + } - if (getBusiness.ok) return (await getBusiness.json()) as Business; + public async getCustomerPaymentMethods(): Promise { + try { + const response = await this._fetchCustomerPaymentMethods(); - throw await buildErrorResponse(getBusiness); - } catch (e) { - throw buildErrorResponseFromCatch(e); + const apms_results = + response && "results" in response && response["results"].length > 0 + ? response["results"] + : []; + + return apms_results + .filter((apmItem) => apmItem.category.toLowerCase() !== "cards") + .map((apmItem) => { + const apm = { + id: apmItem.pk, + payment_method: apmItem.payment_method, + priority: apmItem.priority, + category: apmItem.category, + ...getPaymentMethodDetails(apmItem.payment_method), + }; + return apm; + }) + .sort((a, b) => a.priority - b.priority); + } catch (error) { + throw formatPublicErrorResponse( + { + message: MESSAGES.getPaymentMethodsError, + }, + error, + ); } } - async verify3dsTransaction () { - const result3ds = await this.process3ds.verifyTransactionStatus() - const resultCheckout = await this.resumeCheckout(result3ds) - this.process3ds.setPayload(resultCheckout) - return this.handle3dsRedirect(resultCheckout) + public async getBusiness(): Promise { + try { + return await fetchBusiness( + this.baseUrl, + this.apiKeyTonder, + this.abortController.signal, + ); + } catch (e) { + throw formatPublicErrorResponse( + { + message: MESSAGES.getBusinessError, + }, + e, + ); + } } - async resumeCheckout(response: any) { - // Stop the routing process if the transaction is either hard declined or successful - if (response?.decline?.error_type === "Hard") { - return response + // TODO: DEPRECATED + async getOpenpayDeviceSessionID( + merchant_id: string, + public_key: string, + is_sandbox: boolean, + ): Promise { + try { + return await getOpenpayDeviceSessionID( + merchant_id, + public_key, + is_sandbox, + ); + } catch (e) { + throw buildErrorResponseFromCatch(e); } + } - if (["Success", "Authorized"].includes(response?.transaction_status)) { - return response; - } + // TODO: DEPRECATED + async getSkyflowTokens({ + vault_id, + vault_url, + data, + }: TokensRequest): Promise { + return await getSkyflowTokens({ + vault_id: vault_id, + vault_url: vault_url, + data, + baseUrl: this.baseUrl, + apiKey: this.apiKeyTonder, + }); + } - if (response) { - const routerItems = { - checkout_id: response?.checkout?.id, - }; - try { - const routerResponse = await this.handleCheckoutRouter( - routerItems - ); - return routerResponse - }catch (error){ - // throw error - } - return response - } + _setCartTotal(total: string) { + this.cartTotal = total; } - 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 - // after some the tests in production, since the 3DS process - // doesn't works properly on the sandbox environment - // setTimeout(() => { - // process3ds.verifyTransactionStatus(); - // }, 10000); - this.process3ds.verifyTransactionStatus(); - }).catch((error: any) => { - console.log('Error loading iframe:', error) - }) - } else { - const redirectUrl = this.process3ds.getRedirectUrl() - if (redirectUrl) { - this.process3ds.redirectToChallenge() + async _checkout({ + card, + payment_method, + isSandbox, + // TODO: DEPRECATED + returnUrl: returnUrlData + }: { + card?: ICardFields | string; + payment_method?: string; + isSandbox?: boolean; + returnUrl?: string; + }) { + await this._fetchMerchantData(); + const customer = await this._getCustomer(this.abortController.signal); + const { vault_id, vault_url } = this.merchantData!; + let skyflowTokens; + if (!payment_method || payment_method !== "" || payment_method === null) { + if (typeof card === "string") { + skyflowTokens = { + skyflow_id: card, + }; } else { - return response; + skyflowTokens = await getSkyflowTokens({ + vault_id: vault_id, + vault_url: vault_url, + data: { ...card, card_number: card!.card_number.replace(/\s+/g, "") }, + baseUrl: this.baseUrl, + apiKey: this.apiKeyTonder, + }); } } + + return await this._handleCheckout({ + card: skyflowTokens, + payment_method, + customer, + isSandbox, + returnUrl: returnUrlData + }); } - - async customerRegister(email: string): Promise { + + // TODO: DEPRECATED + async customerRegister( + email: string, + ): Promise { try { - const url = `${this.baseUrlTonder}/api/v1/customer/`; + const url = `${this.baseUrl}/api/v1/customer/`; const data = { email: email }; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Token ${this.publicApiKeyTonder}`, + Authorization: `Token ${this.apiKeyTonder}`, }, - signal: this.signal, + signal: this.abortController.signal, body: JSON.stringify(data), }); - if (response.ok) return await response.json() as CustomerRegisterResponse; + if (response.ok) + return (await response.json()) as CustomerRegisterResponse; throw await buildErrorResponse(response); } catch (e) { throw buildErrorResponseFromCatch(e); } } - async createOrder(orderItems: CreateOrderRequest): Promise { + // TODO: DEPRECATED + async createOrder( + orderItems: CreateOrderRequest, + ): Promise { try { - const url = `${this.baseUrlTonder}/api/v1/orders/`; + const url = `${this.baseUrl}/api/v1/orders/`; const data = orderItems; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Token ${this.publicApiKeyTonder}`, + Authorization: `Token ${this.apiKeyTonder}`, }, body: JSON.stringify(data), }); - if (response.ok) return await response.json() as CreateOrderResponse; + if (response.ok) return (await response.json()) as CreateOrderResponse; throw await buildErrorResponse(response); } catch (e) { throw buildErrorResponseFromCatch(e); } } - async createPayment(paymentItems: CreatePaymentRequest): Promise { + // TODO: DEPRECATED + async createPayment( + paymentItems: CreatePaymentRequest, + ): Promise { try { - const url = `${this.baseUrlTonder}/api/v1/business/${paymentItems.business_pk}/payments/`; + const url = `${this.baseUrl}/api/v1/business/${paymentItems.business_pk}/payments/`; const data = paymentItems; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", - Authorization: `Token ${this.publicApiKeyTonder}`, + Authorization: `Token ${this.apiKeyTonder}`, }, body: JSON.stringify(data), }); - if (response.ok) return await response.json() as CreatePaymentResponse; + if (response.ok) return (await response.json()) as CreatePaymentResponse; throw await buildErrorResponse(response); } catch (e) { throw buildErrorResponseFromCatch(e); } } - async handleCheckoutRouter(routerData: StartCheckoutRequest | StartCheckoutIdRequest){ - try { - const url = `${this.baseUrlTonder}/api/v1/checkout-router/`; - const data = routerData; - const response = await fetch(url, { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Token ${this.publicApiKeyTonder}`, - }, - 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); - } catch (e) { - throw buildErrorResponseFromCatch(e); - } + + // TODO: DEPRECATED + async startCheckoutRouter( + routerData: StartCheckoutRequest | StartCheckoutIdRequest, + ): Promise { + const checkoutResult = await startCheckoutRouter( + this.baseUrl, + this.apiKeyTonder, + routerData, + ); + const payload = await this.init3DSRedirect(checkoutResult); + if (payload) return checkoutResult; } - async startCheckoutRouter(routerData: StartCheckoutRequest | StartCheckoutIdRequest): Promise { - const checkoutResult = await this.handleCheckoutRouter(routerData); - const payload = await this.init3DSRedirect(checkoutResult) - if(payload) - return checkoutResult; + // TODO: DEPRECATED + async init3DSRedirect(checkoutResult: IStartCheckoutResponse) { + this.process3ds.setPayload(checkoutResult); + return await this._handle3dsRedirect(checkoutResult); } - async startCheckoutRouterFull(routerFullData: StartCheckoutFullRequest): Promise { - + // TODO: DEPRECATED + async startCheckoutRouterFull( + routerFullData: StartCheckoutFullRequest, + ): Promise { try { - - const { - order, - total, - customer, - skyflowTokens, - return_url, - isSandbox, - metadata, + const { + order, + total, + customer, + skyflowTokens, + return_url, + isSandbox, + metadata, currency, - payment_method + payment_method, } = routerFullData; - const merchantResult = await this.getBusiness(); - - const customerResult : CustomerRegisterResponse | ErrorResponse = await this.customerRegister(customer.email); + const merchantResult = await this._fetchMerchantData(); - if(customerResult && "auth_token" in customerResult && merchantResult && "reference" in merchantResult) { + const customerResult: CustomerRegisterResponse | ErrorResponse = + await this.customerRegister(customer.email); + if ( + customerResult && + "auth_token" in customerResult && + merchantResult && + "reference" in merchantResult + ) { const orderData: CreateOrderRequest = { - business: this.publicApiKeyTonder, + business: this.apiKeyTonder, client: customerResult.auth_token, billing_address_id: null, shipping_address_id: null, @@ -284,29 +392,30 @@ export class LiteCheckout implements LiteCheckoutConstructor { const dateString = now.toISOString(); - if("id" in orderResult && "id" in customerResult && "business" in merchantResult) { - + if ( + "id" in orderResult && + "id" in customerResult && + "business" in merchantResult + ) { const paymentItems: CreatePaymentRequest = { business_pk: merchantResult.business.pk, amount: total, date: dateString, order_id: orderResult.id, - client_id: customerResult.id + client_id: customerResult.id, }; - const paymentResult = await this.createPayment( - paymentItems - ); + const paymentResult = await this.createPayment(paymentItems); let deviceSessionIdTonder: any; - const { openpay_keys, business } = merchantResult + const { openpay_keys, business } = merchantResult; if (openpay_keys.merchant_id && openpay_keys.public_key) { - deviceSessionIdTonder = await this.getOpenpayDeviceSessionID( + deviceSessionIdTonder = await getOpenpayDeviceSessionID( openpay_keys.merchant_id, openpay_keys.public_key, - isSandbox + isSandbox, ); } @@ -323,192 +432,101 @@ export class LiteCheckout implements LiteCheckoutConstructor { amount: total, title_ship: "shipping", description: "transaction", - device_session_id: deviceSessionIdTonder ? deviceSessionIdTonder : null, + device_session_id: deviceSessionIdTonder + ? deviceSessionIdTonder + : null, token_id: "", - order_id: ("id" in orderResult) && orderResult.id, + order_id: "id" in orderResult && orderResult.id, business_id: business.pk, - payment_id: ("pk" in paymentResult) && paymentResult.pk, - source: 'sdk', + payment_id: "pk" in paymentResult && paymentResult.pk, + source: "sdk", metadata: metadata, browser_info: getBrowserInfo(), currency: currency, - ...( !!payment_method - ? {payment_method} - : {card: skyflowTokens} - ), - ...(typeof MP_DEVICE_SESSION_ID !== "undefined" ? {mp_device_session_id: MP_DEVICE_SESSION_ID}:{}) + ...(!!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); - const payload = await this.init3DSRedirect(checkoutResult) - if(payload) - return checkoutResult; + const checkoutResult = await startCheckoutRouter( + this.baseUrl, + this.apiKeyTonder, + routerItems, + ); + const payload = await this.init3DSRedirect(checkoutResult); + if (payload) return checkoutResult; } else { - throw new ErrorResponse({ code: "500", body: orderResult as any, name: "Keys error", - message: "Order response errors" - } as IErrorResponse) - + message: "Order response errors", + } as IErrorResponse); } - } else { - throw new ErrorResponse({ code: "500", body: merchantResult as any, name: "Keys error", - message: "Merchant or customer reposne errors" - } as IErrorResponse) - + message: "Merchant or customer reposne errors", + } as IErrorResponse); } } catch (e) { - throw buildErrorResponseFromCatch(e); - - } - } - - async init3DSRedirect(checkoutResult: ErrorResponse | StartCheckoutResponse){ - this.process3ds.setPayload(checkoutResult) - return await this.handle3dsRedirect(checkoutResult) - } - - async getSkyflowTokens({ - vault_id, - vault_url, - data, - }: TokensRequest): Promise { - const skyflow = Skyflow.init({ - vaultID: vault_id, - vaultURL: vault_url, - getBearerToken: async () => await this.getVaultToken(), - options: { - logLevel: Skyflow.LogLevel.ERROR, - env: Skyflow.Env.DEV, - }, - }); - - const collectContainer: CollectContainer = skyflow.container( - Skyflow.ContainerType.COLLECT - ) as CollectContainer; - - const fieldPromises = await this.getFieldsPromise(data, collectContainer); - - const result = await Promise.all(fieldPromises); - - const mountFail = result.some((item: boolean) => !item); - - if (mountFail) { - throw buildErrorResponseFromCatch(Error("Ocurrió un error al montar los campos de la tarjeta")); - } else { - try { - const collectResponseSkyflowTonder = await collectContainer.collect() as any; - if (collectResponseSkyflowTonder) return collectResponseSkyflowTonder["records"][0]["fields"]; - throw buildErrorResponseFromCatch(Error("Por favor, verifica todos los campos de tu tarjeta")) - } catch (error) { - throw buildErrorResponseFromCatch(error); - } } } - async getVaultToken(): Promise { + // TODO: DEPRECATED + async registerCustomerCard( + secureToken: string, + customerToken: string, + data: RegisterCustomerCardRequest, + ): Promise { try { - const response = await fetch(`${this.baseUrlTonder}/api/v1/vault-token/`, { - method: "GET", - headers: { - Authorization: `Token ${this.publicApiKeyTonder}`, - }, - signal: this.signal, - }); - if (response.ok) return (await response.json() as GetVaultTokenResponse)?.token; - throw new Error(`HTTPCODE: ${response.status}`) - } catch (e) { - throw new Error(`Failed to retrieve bearer token; ${typeof e == "string" ? e : (e as Error).message}`) - } - } - - async getFieldsPromise(data: any, collectContainer: CollectContainer): Promise[]> { - const fields = await this.getFields(data, collectContainer); - if (!fields) return []; - - return fields.map((field: { element: CollectElement, key: string }) => { - return new Promise((resolve) => { - const div = document.createElement("div"); - div.hidden = true; - div.id = `id-${field.key}`; - document.querySelector(`body`)?.appendChild(div); - setTimeout(() => { - field.element.mount(`#id-${field.key}`); - setInterval(() => { - if (field.element.isMounted()) { - const value = data[field.key]; - field.element.update({ value: value }); - return resolve(field.element.isMounted()); - } - }, 120); - }, 120); - }); - }) - } - - async registerCustomerCard(secureToken: string, customerToken: string, data: RegisterCustomerCardRequest): Promise { - try { - await this.#fetchMerchantData() - - const response = await fetch(`${this.baseUrlTonder}/api/v1/business/${getBusinessId(this.merchantData)}/cards/`, { - method: 'POST', - headers: { - 'Authorization': `Bearer ${secureToken}`, - 'User-token': customerToken, - 'Content-Type': 'application/json' - }, - signal: this.signal, - body: JSON.stringify({...data}) - }); - - if (response.ok) return await response.json() as RegisterCustomerCardResponse; - throw await buildErrorResponse(response); - } catch (error) { - throw buildErrorResponseFromCatch(error); - } - } - - async getCustomerCards(customerToken: string, bearerToken: string): Promise { - try { - await this.#fetchMerchantData() + await this._fetchMerchantData(); - const response = await fetch(`${this.baseUrlTonder}/api/v1/business/${getBusinessId(this.merchantData)}/cards`, { - method: 'GET', - headers: { - 'Authorization': `Bearer ${bearerToken}`, - 'User-Token': customerToken, - 'Content-Type': 'application/json', - 'Accept': 'application/json' + const response = await fetch( + `${this.baseUrl}/api/v1/business/${getBusinessId(this.merchantData)}/cards/`, + { + method: "POST", + headers: { + Authorization: `Bearer ${secureToken}`, + "User-token": customerToken, + "Content-Type": "application/json", + }, + body: JSON.stringify({ ...data }), }, - signal: this.signal, - }); + ); - if (response.ok) return await response.json() as GetCustomerCardsResponse; + if (response.ok) + return (await response.json()) as RegisterCustomerCardResponse; throw await buildErrorResponse(response); } catch (error) { throw buildErrorResponseFromCatch(error); } } - async deleteCustomerCard(customerToken: string, skyflowId: string = ""): Promise { + // TODO: DEPRECATED + async deleteCustomerCard( + customerToken: string, + skyflowId: string = "", + ): Promise { try { - await this.#fetchMerchantData() - const response = await fetch(`${this.baseUrlTonder}/api/v1/business/${getBusinessId(this.merchantData)}/cards/${skyflowId}`, { - method: 'DELETE', - headers: { - 'Authorization': `Token ${customerToken}`, - 'Content-Type': 'application/json' + await this._fetchMerchantData(); + const response = await fetch( + `${this.baseUrl}/api/v1/business/${getBusinessId(this.merchantData)}/cards/${skyflowId}`, + { + method: "DELETE", + headers: { + Authorization: `Token ${customerToken}`, + "Content-Type": "application/json", + }, + signal: this.abortController.signal, }, - signal: this.signal, - }); + ); if (response.ok) return true; throw await buildErrorResponse(response); @@ -517,38 +535,36 @@ export class LiteCheckout implements LiteCheckoutConstructor { } } - private async getFields(data: any, collectContainer: CollectContainer): Promise<{ element: CollectElement, key: string }[]> { - return await Promise.all( - Object.keys(data).map(async (key) => { - const cardHolderNameElement = await collectContainer.create({ - table: "cards", - column: key, - type: Skyflow.ElementType.INPUT_FIELD, - }); - return { element: cardHolderNameElement, key: key }; - }) - ) - } - + // TODO: DEPRECATED async getActiveAPMs(): Promise { try { - const apms_response = await getCustomerAPMs(this.baseUrlTonder, this.publicApiKeyTonder); - const apms_results = apms_response && apms_response['results'] && apms_response['results'].length > 0 ? apms_response['results'] : [] + const apms_response = await getCustomerAPMs( + this.baseUrl, + this.apiKeyTonder, + ); + const apms_results = + apms_response && + apms_response["results"] && + apms_response["results"].length > 0 + ? apms_response["results"] + : []; this.activeAPMs = apms_results - .filter((apmItem: TonderAPM) => - apmItem.category.toLowerCase() !== 'cards') + .filter( + (apmItem: TonderAPM) => apmItem.category.toLowerCase() !== "cards", + ) .map((apmItem: TonderAPM) => { const apm: APM = { id: apmItem.pk, payment_method: apmItem.payment_method, priority: apmItem.priority, category: apmItem.category, - ...getPaymentMethodDetails(apmItem.payment_method,) - } + ...getPaymentMethodDetails(apmItem.payment_method), + }; return apm; - }).sort((a: APM, b: APM) => a.priority - b.priority); + }) + .sort((a: APM, b: APM) => a.priority - b.priority); - return this.activeAPMs + return this.activeAPMs; } catch (e) { console.error("Error getting APMS", e); return []; @@ -557,13 +573,13 @@ export class LiteCheckout implements LiteCheckoutConstructor { async getSecureToken(token: string): Promise { try { - const response = await fetch(`${this.baseUrlTonder}/api/secure-token/`, { + const response = await fetch(`${this.baseUrl}/api/secure-token/`, { method: 'POST', headers: { 'Authorization': `Token ${token}`, 'Content-Type': 'application/json' }, - signal: this.signal, + signal: this.abortController.signal }); if (response.ok) return await response.json() as GetSecureTokenResponse; diff --git a/src/data/businessApi.ts b/src/data/businessApi.ts new file mode 100644 index 0000000..51e27f6 --- /dev/null +++ b/src/data/businessApi.ts @@ -0,0 +1,18 @@ +import {GetBusinessResponse} from "../types/responses"; + +export async function fetchBusiness( + baseUrl: string, + apiKey: string, + signal: AbortSignal, +): Promise { + const getBusiness = await fetch( + `${baseUrl}/api/v1/payments/business/${apiKey}`, + { + headers: { + Authorization: `Token ${apiKey}`, + }, + signal: signal, + }, + ); + return await getBusiness.json(); +} diff --git a/src/data/cardApi.ts b/src/data/cardApi.ts new file mode 100644 index 0000000..813943e --- /dev/null +++ b/src/data/cardApi.ts @@ -0,0 +1,87 @@ +import { + buildErrorResponse, + buildErrorResponseFromCatch, +} from "../helpers/utils"; +import { MESSAGES } from "../shared/constants/messages"; +import {ICustomerCardsResponse, ISaveCardResponse, ISaveCardSkyflowRequest} from "../types/card"; + +export async function fetchCustomerCards( + baseUrl: string, + customerToken: string, + businessId: string | number, + signal = null, +): Promise { + try { + const url = `${baseUrl}/api/v1/business/${businessId}/cards/`; + const response = await fetch(url, { + method: "GET", + headers: { + Authorization: `Token ${customerToken}`, + "Content-Type": "application/json", + }, + signal, + }); + if (response.ok) return await response.json(); + const res_json = await response.json(); + + throw await buildErrorResponse(response, res_json); + } catch (error) { + throw buildErrorResponseFromCatch(error); + } +} + +export async function saveCustomerCard( + baseUrl: string, + secureToken: string, + customerToken: string, + businessId: string | number, + data: ISaveCardSkyflowRequest, +): Promise { + try { + const url = `${baseUrl}/api/v1/business/${businessId}/cards/`; + const response = await fetch(url, { + method: "POST", + headers: { + Authorization: `Bearer ${secureToken}`, + "Content-Type": "application/json", + 'User-token': customerToken, + }, + body: JSON.stringify(data), + }); + + if (response.ok) return await response.json(); + + const res_json = await response.json(); + + throw await buildErrorResponse(response, res_json); + } catch (error) { + throw buildErrorResponseFromCatch(error); + } +} + +export async function removeCustomerCard( + baseUrl: string, + customerToken: string, + skyflowId = "", + businessId: string | number, +): Promise { + try { + const url = `${baseUrl}/api/v1/business/${businessId}/cards/${skyflowId}`; + + const response = await fetch(url, { + method: "DELETE", + headers: { + Authorization: `Token ${customerToken}`, + "Content-Type": "application/json", + }, + }); + + if (response.status === 204) return MESSAGES.cardSaved; + if (response.ok && "json" in response) return await response.json(); + const res_json = await response.json(); + + throw await buildErrorResponse(response, res_json); + } catch (error) { + throw buildErrorResponseFromCatch(error); + } +} diff --git a/src/data/checkoutApi.ts b/src/data/checkoutApi.ts new file mode 100644 index 0000000..2688ba3 --- /dev/null +++ b/src/data/checkoutApi.ts @@ -0,0 +1,84 @@ +import {CreateOrderRequest, CreatePaymentRequest} from "../types/requests"; +import {IStartCheckoutIdRequest, IStartCheckoutRequest} from "../types/checkout"; + +declare const MP_DEVICE_SESSION_ID: string | undefined; + + +export async function createOrder( + baseUrl: string, + apiKey: string, + orderItems: CreateOrderRequest, +) { + const url = `${baseUrl}/api/v1/orders/`; + const data = orderItems; + const response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${apiKey}`, + }, + body: JSON.stringify(data), + }); + if (response.status === 201) { + return await response.json(); + } else { + throw new Error(`Error: ${response.statusText}`); + } +} + +export async function createPayment( + baseUrl: string, + apiKey: string, + paymentItems: CreatePaymentRequest, +) { + const url = `${baseUrl}/api/v1/business/${paymentItems.business_pk}/payments/`; + const data = paymentItems; + const response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${apiKey}`, + }, + body: JSON.stringify(data), + }); + if (response.status >= 200 && response.status <= 299) { + return await response.json(); + } else { + throw new Error(`Error: ${response.statusText}`); + } +} + +export async function startCheckoutRouter( + baseUrl: string, + apiKey: string, + routerItems: IStartCheckoutRequest | IStartCheckoutIdRequest, +) { + try { + const url = `${baseUrl}/api/v1/checkout-router/`; + const data = routerItems; + const response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${apiKey}`, + }, + body: JSON.stringify({ + ...data, + ...(typeof MP_DEVICE_SESSION_ID !== "undefined" + ? { mp_device_session_id: MP_DEVICE_SESSION_ID } + : {}), + }), + }); + if (response.status >= 200 && response.status <= 299) { + return await response.json(); + } else { + const errorResponse = await response.json(); + const error = new Error("Failed to start checkout router"); + // @ts-ignore + error.details = errorResponse; + throw error; + } + } catch (error) { + throw error; + } +} diff --git a/src/data/customerApi.ts b/src/data/customerApi.ts new file mode 100644 index 0000000..fcb2b83 --- /dev/null +++ b/src/data/customerApi.ts @@ -0,0 +1,31 @@ +import {CustomerRegisterResponse} from "../types/responses"; + +export async function registerOrFetchCustomer( + baseUrl: string, + apiKey: string, + customer: Record, + signal: AbortSignal | null = null, +): Promise { + const url = `${baseUrl}/api/v1/customer/`; + const data = { + email: customer.email, + first_name: customer?.firstName, + last_name: customer?.lastName, + phone: customer?.phone, + }; + const response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${apiKey}`, + }, + signal: signal, + body: JSON.stringify(data), + }); + + if (response.status === 201) { + return await response.json(); + } else { + throw new Error(`Error: ${response.statusText}`); + } +} diff --git a/src/data/openPayApi.ts b/src/data/openPayApi.ts new file mode 100644 index 0000000..8d56e91 --- /dev/null +++ b/src/data/openPayApi.ts @@ -0,0 +1,12 @@ +export async function getOpenpayDeviceSessionID( + merchant_id: string, + public_key: string, + isSandbox: boolean = true, + signal: AbortSignal | null = null, +): Promise { + let openpay = await window.OpenPay; + openpay.setId(merchant_id); + openpay.setApiKey(public_key); + openpay.setSandboxMode(isSandbox); + return await openpay.deviceData.setup({ signal }); +} diff --git a/src/data/paymentMethodApi.ts b/src/data/paymentMethodApi.ts new file mode 100644 index 0000000..52d5b36 --- /dev/null +++ b/src/data/paymentMethodApi.ts @@ -0,0 +1,37 @@ +import { + buildErrorResponse, + buildErrorResponseFromCatch, +} from "../helpers/utils"; +import {IPaymentMethodResponse} from "../types/paymentMethod"; + +export async function fetchCustomerPaymentMethods( + baseUrl: string, + apiKey: string, + params = { + status: "active", + pagesize: "10000", + }, + signal = null, +): Promise { + try { + const queryString = new URLSearchParams(params).toString(); + + const response = await fetch( + `${baseUrl}/api/v1/payment_methods?${queryString}`, + { + method: "GET", + headers: { + Authorization: `Token ${apiKey}`, + "Content-Type": "application/json", + }, + signal, + }, + ); + + if (response.ok) return await response.json(); + const res_json = await response.json(); + throw await buildErrorResponse(response, res_json); + } catch (error) { + throw buildErrorResponseFromCatch(error); + } +} diff --git a/src/data/skyflowApi.ts b/src/data/skyflowApi.ts new file mode 100644 index 0000000..49256c0 --- /dev/null +++ b/src/data/skyflowApi.ts @@ -0,0 +1,20 @@ +export async function getVaultToken( + baseUrl: string, + apiKey: string, + signal = null, +) { + const response = await fetch(`${baseUrl}/api/v1/vault-token/`, { + method: "GET", + headers: { + Authorization: `Token ${apiKey}`, + }, + signal: signal, + }); + + if (response.ok) { + const responseBody = await response.json(); + return responseBody.token; + } else { + throw new Error("Failed to retrieve bearer token"); + } +} diff --git a/src/helpers/mercadopago.ts b/src/helpers/mercadopago.ts index 34e6936..861ac9a 100644 --- a/src/helpers/mercadopago.ts +++ b/src/helpers/mercadopago.ts @@ -1,16 +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); - } + 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/skyflow.ts b/src/helpers/skyflow.ts new file mode 100644 index 0000000..a50fbe2 --- /dev/null +++ b/src/helpers/skyflow.ts @@ -0,0 +1,91 @@ +import {ErrorResponse} from "../classes/errorResponse"; +import Skyflow from "skyflow-js"; +import CollectContainer from "skyflow-js/types/core/external/collect/collect-container"; +import {buildErrorResponseFromCatch} from "./utils"; +import CollectElement from "skyflow-js/types/core/external/collect/collect-element"; +import {getVaultToken} from "../data/skyflowApi"; +import {TokensSkyflowRequest} from "../types/requests"; + +export async function getSkyflowTokens({ + baseUrl, + apiKey, + vault_id, + vault_url, + data, +}: TokensSkyflowRequest): Promise { + const skyflow = Skyflow.init({ + vaultID: vault_id, + vaultURL: vault_url, + getBearerToken: async () => await getVaultToken(baseUrl, apiKey), + options: { + logLevel: Skyflow.LogLevel.ERROR, + env: Skyflow.Env.DEV, + }, + }); + + const collectContainer: CollectContainer = skyflow.container( + Skyflow.ContainerType.COLLECT, + ) as CollectContainer; + + const fieldPromises = await getFieldsPromise(data, collectContainer); + + const result = await Promise.all(fieldPromises); + + const mountFail = result.some((item: boolean) => !item); + + if (mountFail) { + throw buildErrorResponseFromCatch( + Error("Ocurrió un error al montar los campos de la tarjeta"), + ); + } else { + try { + const collectResponseSkyflowTonder = + (await collectContainer.collect()) as any; + if (collectResponseSkyflowTonder) + return collectResponseSkyflowTonder["records"][0]["fields"]; + throw buildErrorResponseFromCatch( + Error("Por favor, verifica todos los campos de tu tarjeta"), + ); + } catch (error) { + throw buildErrorResponseFromCatch(error); + } + } +} + +async function getFieldsPromise(data: any, collectContainer: CollectContainer): Promise[]> { + const fields = await getFields(data, collectContainer); + if (!fields) return []; + +return fields.map((field: { element: CollectElement, key: string }) => { + return new Promise((resolve) => { + const div = document.createElement("div"); + div.hidden = true; + div.id = `id-${field.key}`; + document.querySelector(`body`)?.appendChild(div); + setTimeout(() => { + field.element.mount(`#id-${field.key}`); + setInterval(() => { + if (field.element.isMounted()) { + const value = data[field.key]; + field.element.update({ value: value }); + return resolve(field.element.isMounted()); + } + }, 120); + }, 120); + }); +}) +} + +async function getFields(data: any, collectContainer: CollectContainer): Promise<{ element: CollectElement, key: string }[]> { + return await Promise.all( + Object.keys(data).map(async (key) => { + const cardHolderNameElement = await collectContainer.create({ + table: "cards", + column: key, + type: Skyflow.ElementType.INPUT_FIELD, + }); + return { element: cardHolderNameElement, key: key }; + }) + ) +} + diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index b053b96..10b14b9 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -1,41 +1,47 @@ import { ErrorResponse } from "../classes/errorResponse"; -import { IErrorResponse } from "../types/responses"; -import { PAYMENT_METHOD } from "./constants"; +import {IErrorResponse} from "../types/responses"; export const getBrowserInfo = () => { const browserInfo = { - javascript_enabled: true, // Assumed since JavaScript is running + javascript_enabled: true, // Assumed since JavaScript is running time_zone: new Date().getTimezoneOffset(), - language: navigator.language || 'en-US', // Fallback to 'en-US' + language: navigator.language || "en-US", // Fallback to 'en-US' color_depth: window.screen ? window.screen.colorDepth : null, - screen_width: window.screen ? window.screen.width * window.devicePixelRatio || window.screen.width : null, - screen_height: window.screen ? window.screen.height * window.devicePixelRatio || window.screen.height : null, + screen_width: window.screen + ? window.screen.width * window.devicePixelRatio || window.screen.width + : null, + screen_height: window.screen + ? window.screen.height * window.devicePixelRatio || window.screen.height + : null, user_agent: navigator.userAgent, }; return browserInfo; -} +}; -export const getBusinessId = (merchantData: any) =>{ - return merchantData && "business" in merchantData ? merchantData?.business?.pk:"" -} +export const getBusinessId = (merchantData: any) => { + return merchantData && "business" in merchantData + ? merchantData?.business?.pk + : ""; +}; const buildErrorResponseFromCatch = (e: any): ErrorResponse => { - const error = new ErrorResponse({ code: e?.status ? e.status : e.code, body: e?.body, - name: e ? typeof e == "string" ? "catch" : (e as Error).name : "Error", + name: e ? (typeof e == "string" ? "catch" : (e as Error).name) : "Error", message: e ? (typeof e == "string" ? e : (e as Error).message) : "Error", stack: typeof e == "string" ? undefined : (e as Error).stack, - }) + }); return error; -} +}; const buildErrorResponse = async ( response: Response, - stack: string | undefined = undefined + stack: string | undefined = undefined, ): Promise => { - let body, status, message = "Error"; + let body, + status, + message = "Error"; if (response && "json" in response) { body = await response?.json(); @@ -49,8 +55,8 @@ const buildErrorResponse = async ( message = await response.text(); } - if(body?.detail){ - message = body.detail + if (body?.detail) { + message = body.detail; } const error = new ErrorResponse({ code: status, @@ -58,263 +64,57 @@ const buildErrorResponse = async ( name: status, message: message, stack, - } as IErrorResponse) + } as IErrorResponse); return error; -} - -const getPaymentMethodDetails = (scheme_data: string): {icon: string; label: string} => { - const scheme: PAYMENT_METHOD = clearSpace(scheme_data.toUpperCase()) as PAYMENT_METHOD; - - const PAYMENT_METHODS_CATALOG: Partial> = { - [PAYMENT_METHOD.SORIANA]: { - label: "Soriana", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/soriana.png", - }, - [PAYMENT_METHOD.OXXO]: { - label: "Oxxo", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/oxxo.png", - }, - [PAYMENT_METHOD.CODI]: { - label: "CoDi", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/codi.png", - }, - [PAYMENT_METHOD.SPEI]: { - label: "SPEI", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/spei.png", - }, - [PAYMENT_METHOD.PAYPAL]: { - label: "Paypal", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/paypal.png", - }, - [PAYMENT_METHOD.COMERCIALMEXICANA]: { - label: "Comercial Mexicana", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/comercial_exicana.png", - }, - [PAYMENT_METHOD.BANCOMER]: { - label: "Bancomer", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/bancomer.png", - }, - [PAYMENT_METHOD.WALMART]: { - label: "Walmart", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/walmart.png", - }, - [PAYMENT_METHOD.BODEGA]: { - label: "Bodega Aurrera", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/bodega_aurrera.png", - }, - [PAYMENT_METHOD.SAMSCLUB]: { - label: "Sam´s Club", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/sams_club.png", - }, - [PAYMENT_METHOD.SUPERAMA]: { - label: "Superama", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/superama.png", - }, - [PAYMENT_METHOD.CALIMAX]: { - label: "Calimax", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/calimax.png", - }, - [PAYMENT_METHOD.EXTRA]: { - label: "Tiendas Extra", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/tiendas_extra.png", - }, - [PAYMENT_METHOD.CIRCULOK]: { - label: "Círculo K", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/circulo_k.png", - }, - [PAYMENT_METHOD.SEVEN11]: { - label: "7 Eleven", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/7_eleven.png", - }, - [PAYMENT_METHOD.TELECOMM]: { - label: "Telecomm", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/telecomm.png", - }, - [PAYMENT_METHOD.BANORTE]: { - label: "Banorte", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/banorte.png", - }, - [PAYMENT_METHOD.BENAVIDES]: { - label: "Farmacias Benavides", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_benavides.png", - }, - [PAYMENT_METHOD.DELAHORRO]: { - label: "Farmacias del Ahorro", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_ahorro.png", - }, - [PAYMENT_METHOD.ELASTURIANO]: { - label: "El Asturiano", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/asturiano.png", - }, - [PAYMENT_METHOD.WALDOS]: { - label: "Waldos", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/waldos.png", - }, - [PAYMENT_METHOD.ALSUPER]: { - label: "Alsuper", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/al_super.png", - }, - [PAYMENT_METHOD.KIOSKO]: { - label: "Kiosko", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/kiosko.png", - }, - [PAYMENT_METHOD.STAMARIA]: { - label: "Farmacias Santa María", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_santa_maria.png", - }, - [PAYMENT_METHOD.LAMASBARATA]: { - label: "Farmacias la más barata", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_barata.png", - }, - [PAYMENT_METHOD.FARMROMA]: { - label: "Farmacias Roma", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_roma.png", - }, - [PAYMENT_METHOD.FARMUNION]: { - label: "Pago en Farmacias Unión", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_union.png", - }, - [PAYMENT_METHOD.FARMATODO]: { - label: "Pago en Farmacias Farmatodo", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_farmatodo.png ", - }, - [PAYMENT_METHOD.SFDEASIS]: { - label: "Pago en Farmacias San Francisco de Asís", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_san_francisco.png", - }, - [PAYMENT_METHOD.FARM911]: { - label: "Farmacias 911", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.FARMECONOMICAS]: { - label: "Farmacias Economicas", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.FARMMEDICITY]: { - label: "Farmacias Medicity", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.RIANXEIRA]: { - label: "Rianxeira", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.WESTERNUNION]: { - label: "Western Union", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.ZONAPAGO]: { - label: "Zona Pago", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.CAJALOSANDES]: { - label: "Caja Los Andes", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.CAJAPAITA]: { - label: "Caja Paita", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.CAJASANTA]: { - label: "Caja Santa", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.CAJASULLANA]: { - label: "Caja Sullana", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.CAJATRUJILLO]: { - label: "Caja Trujillo", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.EDPYME]: { - label: "Edpyme", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.KASNET]: { - label: "KasNet", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.NORANDINO]: { - label: "Norandino", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.QAPAQ]: { - label: "Qapaq", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.RAIZ]: { - label: "Raiz", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.PAYSER]: { - label: "Paysera", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.WUNION]: { - label: "Western Union", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.BANCOCONTINENTAL]: { - label: "Banco Continental", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.GMONEY]: { - label: "Go money", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.GOPAY]: { - label: "Go pay", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.WU]: { - label: "Western Union", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.PUNTOSHEY]: { - label: "Puntoshey", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.AMPM]: { - label: "Ampm", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.JUMBOMARKET]: { - label: "Jumbomarket", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.SMELPUEBLO]: { - label: "Smelpueblo", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.BAM]: { - label: "Bam", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.REFACIL]: { - label: "Refacil", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, - [PAYMENT_METHOD.ACYVALORES]: { - label: "Acyvalores", - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - }, +}; + +function formatPublicErrorResponse(data: Record, error: any) { + let code = 200; + try { + code = Number(error?.code || 200); + } catch {} + + const default_res = { + status: "error", + code, + message: "", + detail: + error?.body?.detail || + error?.body?.error || + error.body || + "Ocurrio un error inesperado.", }; - const _default = { - icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", - label: "" + return { + ...default_res, + ...data, }; - - return PAYMENT_METHODS_CATALOG[scheme] || _default; } const clearSpace = (text: string) => { - return text.trim().replace(/\s+/g, ''); -} + return text.trim().replace(/\s+/g, ""); +}; + +const getCardType = (scheme: string) => { + if (scheme === "Visa") { + // Check if visa + return "https://d35a75syrgujp0.cloudfront.net/cards/visa.png"; + } else if (scheme === "Mastercard") { + // Check if master + return "https://d35a75syrgujp0.cloudfront.net/cards/mastercard.png"; + } else if (scheme === "American Express") { + // Check if amex + return "https://d35a75syrgujp0.cloudfront.net/cards/american_express.png"; + } else { + return "https://d35a75syrgujp0.cloudfront.net/cards/default_card.png"; + } +}; export { buildErrorResponseFromCatch, buildErrorResponse, - getPaymentMethodDetails -} \ No newline at end of file + getCardType, + formatPublicErrorResponse, + clearSpace, +}; diff --git a/src/helpers/validations.ts b/src/helpers/validations.ts new file mode 100644 index 0000000..06045b2 --- /dev/null +++ b/src/helpers/validations.ts @@ -0,0 +1,55 @@ +export function validateCardNumber(cardNumber: string) { + const regex = /^\d{12,19}$/; + return regex.test(cardNumber) && luhnCheck(cardNumber); +} + +export function validateCardholderName(name: string) { + const regex = /^([a-zA-Z\\ \\,\\.\\-\\']{2,})$/; + return regex.test(name); +} + +export function validateCVV(cvv: string) { + const regex = /^\d{3,4}$/; + return regex.test(cvv); +} + +export function validateExpirationDate(expirationDate: string) { + const regex = /^(0[1-9]|1[0-2])\/\d{2}$/; + if (!regex.test(expirationDate)) { + return false; + } + const [month, year] = expirationDate.split("/"); + const currentDate = new Date(); + // @ts-ignore + const expiration = new Date(`20${year}`, month - 1); + return expiration >= currentDate; +} + +export function validateExpirationMonth(month: string) { + const regex = /^(0[1-9]|1[0-2])$/; + return regex.test(month); +} + +export function validateExpirationYear(year: string) { + const regex = /^\d{2}$/; + if (!regex.test(year)) { + return false; + } + const currentYear = new Date().getFullYear() % 100; + return parseInt(year, 10) >= currentYear; +} + +const luhnCheck = (num: number | string) => { + const arr = `${num}` + .split("") + .reverse() + .map((x) => Number.parseInt(x)); + const lastDigit = arr.shift(); + let sum = arr.reduce( + (acc, val, i) => + i % 2 !== 0 ? acc + val : acc + ((val *= 2) > 9 ? val - 9 : val), + 0, + ); + sum += lastDigit!; + return sum % 10 === 0; +}; diff --git a/src/index.ts b/src/index.ts index 5e1ac78..64cf8e5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,13 @@ import { LiteCheckout } from './classes/liteCheckout' +import { BaseInlineCheckout } from './classes/BaseInlineCheckout' +import { validateCVV, validateCardNumber, validateExpirationMonth, validateCardholderName, validateExpirationYear } from './helpers/validations' export { - LiteCheckout + LiteCheckout, + BaseInlineCheckout, + validateCVV, + validateCardNumber, + validateCardholderName, + validateExpirationMonth, + validateExpirationYear } \ No newline at end of file diff --git a/src/shared/catalog/paymentMethodsCatalog.ts b/src/shared/catalog/paymentMethodsCatalog.ts new file mode 100644 index 0000000..24dd14f --- /dev/null +++ b/src/shared/catalog/paymentMethodsCatalog.ts @@ -0,0 +1,248 @@ +import {clearSpace} from "../../helpers/utils"; +import {PAYMENT_METHOD_APM} from "../constants/paymentMethodAPM"; + +const PAYMENT_METHODS_CATALOG = { + [PAYMENT_METHOD_APM.SORIANA]: { + label: "Soriana", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/soriana.png", + }, + [PAYMENT_METHOD_APM.OXXO]: { + label: "Oxxo", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/oxxo.png", + }, + [PAYMENT_METHOD_APM.CODI]: { + label: "CoDi", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/codi.png", + }, + [PAYMENT_METHOD_APM.SPEI]: { + label: "SPEI", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/spei.png", + }, + [PAYMENT_METHOD_APM.PAYPAL]: { + label: "Paypal", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/paypal.png", + }, + [PAYMENT_METHOD_APM.COMERCIALMEXICANA]: { + label: "Comercial Mexicana", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/comercial_exicana.png", + }, + [PAYMENT_METHOD_APM.BANCOMER]: { + label: "Bancomer", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/bancomer.png", + }, + [PAYMENT_METHOD_APM.WALMART]: { + label: "Walmart", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/walmart.png", + }, + [PAYMENT_METHOD_APM.BODEGA]: { + label: "Bodega Aurrera", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/bodega_aurrera.png", + }, + [PAYMENT_METHOD_APM.SAMSCLUB]: { + label: "Sam´s Club", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/sams_club.png", + }, + [PAYMENT_METHOD_APM.SUPERAMA]: { + label: "Superama", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/superama.png", + }, + [PAYMENT_METHOD_APM.CALIMAX]: { + label: "Calimax", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/calimax.png", + }, + [PAYMENT_METHOD_APM.EXTRA]: { + label: "Tiendas Extra", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/tiendas_extra.png", + }, + [PAYMENT_METHOD_APM.CIRCULOK]: { + label: "Círculo K", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/circulo_k.png", + }, + [PAYMENT_METHOD_APM.SEVEN11]: { + label: "7 Eleven", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/7_eleven.png", + }, + [PAYMENT_METHOD_APM.TELECOMM]: { + label: "Telecomm", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/telecomm.png", + }, + [PAYMENT_METHOD_APM.BANORTE]: { + label: "Banorte", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/banorte.png", + }, + [PAYMENT_METHOD_APM.BENAVIDES]: { + label: "Farmacias Benavides", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_benavides.png", + }, + [PAYMENT_METHOD_APM.DELAHORRO]: { + label: "Farmacias del Ahorro", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_ahorro.png", + }, + [PAYMENT_METHOD_APM.ELASTURIANO]: { + label: "El Asturiano", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/asturiano.png", + }, + [PAYMENT_METHOD_APM.WALDOS]: { + label: "Waldos", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/waldos.png", + }, + [PAYMENT_METHOD_APM.ALSUPER]: { + label: "Alsuper", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/al_super.png", + }, + [PAYMENT_METHOD_APM.KIOSKO]: { + label: "Kiosko", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/kiosko.png", + }, + [PAYMENT_METHOD_APM.STAMARIA]: { + label: "Farmacias Santa María", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_santa_maria.png", + }, + [PAYMENT_METHOD_APM.LAMASBARATA]: { + label: "Farmacias la más barata", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_barata.png", + }, + [PAYMENT_METHOD_APM.FARMROMA]: { + label: "Farmacias Roma", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_roma.png", + }, + [PAYMENT_METHOD_APM.FARMUNION]: { + label: "Pago en Farmacias Unión", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_union.png", + }, + [PAYMENT_METHOD_APM.FARMATODO]: { + label: "Pago en Farmacias Farmatodo", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_farmatodo.png ", + }, + [PAYMENT_METHOD_APM.SFDEASIS]: { + label: "Pago en Farmacias San Francisco de Asís", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/farmacias_san_francisco.png", + }, + [PAYMENT_METHOD_APM.FARM911]: { + label: "Farmacias 911", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.FARMECONOMICAS]: { + label: "Farmacias Economicas", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.FARMMEDICITY]: { + label: "Farmacias Medicity", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.RIANXEIRA]: { + label: "Rianxeira", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.WESTERNUNION]: { + label: "Western Union", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.ZONAPAGO]: { + label: "Zona Pago", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.CAJALOSANDES]: { + label: "Caja Los Andes", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.CAJAPAITA]: { + label: "Caja Paita", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.CAJASANTA]: { + label: "Caja Santa", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.CAJASULLANA]: { + label: "Caja Sullana", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.CAJATRUJILLO]: { + label: "Caja Trujillo", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.EDPYME]: { + label: "Edpyme", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.KASNET]: { + label: "KasNet", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.NORANDINO]: { + label: "Norandino", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.QAPAQ]: { + label: "Qapaq", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.RAIZ]: { + label: "Raiz", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.PAYSER]: { + label: "Paysera", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.WUNION]: { + label: "Western Union", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.BANCOCONTINENTAL]: { + label: "Banco Continental", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.GMONEY]: { + label: "Go money", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.GOPAY]: { + label: "Go pay", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.WU]: { + label: "Western Union", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.PUNTOSHEY]: { + label: "Puntoshey", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.AMPM]: { + label: "Ampm", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.JUMBOMARKET]: { + label: "Jumbomarket", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.SMELPUEBLO]: { + label: "Smelpueblo", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.BAM]: { + label: "Bam", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.REFACIL]: { + label: "Refacil", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, + [PAYMENT_METHOD_APM.ACYVALORES]: { + label: "Acyvalores", + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + }, +}; + + +export const getPaymentMethodDetails = (scheme_data: string) => { + const scheme: string = clearSpace(scheme_data.toUpperCase()); + const _default = { + icon: "https://d35a75syrgujp0.cloudfront.net/payment_methods/store.png", + label: "", + }; + // @ts-ignore + return PAYMENT_METHODS_CATALOG[scheme] || _default; +}; \ No newline at end of file diff --git a/src/shared/constants/messages.ts b/src/shared/constants/messages.ts new file mode 100644 index 0000000..013ca32 --- /dev/null +++ b/src/shared/constants/messages.ts @@ -0,0 +1,11 @@ +export const MESSAGES = Object.freeze({ + saveCardError: "Ha ocurrido un error guardando la tarjeta. Inténtalo nuevamente.", + removeCardError: "Ha ocurrido un error eliminado la tarjeta. Inténtalo nuevamente.", + getCardsError: "Ha ocurrido un error obteniendo las tarjetas del customer. Inténtalo nuevamente.", + cardExist: "La tarjeta fue registrada previamente.", + removedCard: "Card deleted successfully", + errorCheckout: "No se ha podido procesar el pago", + cardSaved: "Tarjeta registrada con éxito.", + getPaymentMethodsError: "Ha ocurrido un error obteniendo las métodos de pago del customer. Inténtalo nuevamente.", + getBusinessError: "Ha ocurrido un error obteniendo los datos del comercio. Inténtalo nuevamente." +}) \ No newline at end of file diff --git a/src/shared/constants/paymentMethodAPM.ts b/src/shared/constants/paymentMethodAPM.ts new file mode 100644 index 0000000..fe21505 --- /dev/null +++ b/src/shared/constants/paymentMethodAPM.ts @@ -0,0 +1,63 @@ +const PAYMENT_METHOD_APM = Object.freeze({ + SORIANA: "SORIANA", + OXXO: "OXXO", + SPEI: "SPEI", + CODI: "CODI", + MERCADOPAGO: "MERCADOPAGO", + PAYPAL: "PAYPAL", + COMERCIALMEXICANA: "COMERCIALMEXICANA", + BANCOMER: "BANCOMER", + WALMART: "WALMART", + BODEGA: "BODEGA", + SAMSCLUB: "SAMSCLUB", + SUPERAMA: "SUPERAMA", + CALIMAX: "CALIMAX", + EXTRA: "EXTRA", + CIRCULOK: "CIRCULOK", + SEVEN11: "7ELEVEN", + TELECOMM: "TELECOMM", + BANORTE: "BANORTE", + BENAVIDES: "BENAVIDES", + DELAHORRO: "DELAHORRO", + ELASTURIANO: "ELASTURIANO", + WALDOS: "WALDOS", + ALSUPER: "ALSUPER", + KIOSKO: "KIOSKO", + STAMARIA: "STAMARIA", + LAMASBARATA: "LAMASBARATA", + FARMROMA: "FARMROMA", + FARMUNION: "FARMUNION", + FARMATODO: "FARMATODO", + SFDEASIS: "SFDEASIS", + FARM911: "FARM911", + FARMECONOMICAS: "FARMECONOMICAS", + FARMMEDICITY: "FARMMEDICITY", + RIANXEIRA: "RIANXEIRA", + WESTERNUNION: "WESTERNUNION", + ZONAPAGO: "ZONAPAGO", + CAJALOSANDES: "CAJALOSANDES", + CAJAPAITA: "CAJAPAITA", + CAJASANTA: "CAJASANTA", + CAJASULLANA: "CAJASULLANA", + CAJATRUJILLO: "CAJATRUJILLO", + EDPYME: "EDPYME", + KASNET: "KASNET", + NORANDINO: "NORANDINO", + QAPAQ: "QAPAQ", + RAIZ: "RAIZ", + PAYSER: "PAYSER", + WUNION: "WUNION", + BANCOCONTINENTAL: "BANCOCONTINENTAL", + GMONEY: "GMONEY", + GOPAY: "GOPAY", + WU: "WU", + PUNTOSHEY: "PUNTOSHEY", + AMPM: "AMPM", + JUMBOMARKET: "JUMBOMARKET", + SMELPUEBLO: "SMELPUEBLO", + BAM: "BAM", + REFACIL: "REFACIL", + ACYVALORES: "ACYVALORES", +}); + +export { PAYMENT_METHOD_APM }; diff --git a/src/shared/constants/tonderUrl.ts b/src/shared/constants/tonderUrl.ts new file mode 100644 index 0000000..3d0095a --- /dev/null +++ b/src/shared/constants/tonderUrl.ts @@ -0,0 +1,8 @@ +const TONDER_URL_BY_MODE = Object.freeze({ + production: "https://app.tonder.io", + sandbox: "https://sandbox.tonder.io", + stage: "https://stage.tonder.io", + development: "http://localhost:8000", +}); + +export { TONDER_URL_BY_MODE }; diff --git a/src/types/card.ts b/src/types/card.ts new file mode 100644 index 0000000..8a3b2e7 --- /dev/null +++ b/src/types/card.ts @@ -0,0 +1,35 @@ +export interface ICard { + fields: ICardSkyflowFields; + icon?: string; +} + +export interface ICardSkyflowFields { + card_number: string; + expiration_month: string; + expiration_year: string; + skyflow_id: string; + card_scheme: string; + cardholder_name: string; +} + +export interface ICustomerCardsResponse { + user_id: number; + cards: ICard[]; +} + +export interface ISaveCardResponse { + skyflow_id: string; + user_id: number; +} + +export interface ISaveCardSkyflowRequest { + skyflow_id: string; +} + +export interface ISaveCardRequest { + card_number: string; + cvv: string; + expiration_month: string; + expiration_year: string; + cardholder_name: string; +} diff --git a/src/types/checkout.ts b/src/types/checkout.ts new file mode 100644 index 0000000..2cd02f2 --- /dev/null +++ b/src/types/checkout.ts @@ -0,0 +1,124 @@ +import {ICustomer} from "./customer"; + +export interface IStartCheckoutRequestBase { + name: any; + last_name: string; + email_client: any; + phone_number: any; + return_url?: string; + id_product: string; + quantity_product: number; + id_ship: string; + instance_id_ship: string; + amount: any; + title_ship: string; + description: string; + device_session_id: any; + token_id: string; + order_id: any; + business_id: any; + payment_id: any; + source: string; + browser_info?: any; + metadata: any; + currency: string; +} + +export type IStartCheckoutRequestWithCard = IStartCheckoutRequestBase & { + card: any; + payment_method?: never; +}; + +export type IStartCheckoutRequestWithPaymentMethod = + IStartCheckoutRequestBase & { + card?: never; + payment_method: string; +}; + +export type IStartCheckoutRequest = + | IStartCheckoutRequestWithCard + | IStartCheckoutRequestWithPaymentMethod; + +export interface IStartCheckoutIdRequest { + checkout_id: string; +} + +export interface IStartCheckoutErrorResponse { + status: string; + message: string; + psp_response: [ + { + status: number; + response: Object; + }, + ]; + checkout_id: string; + is_route_finished: boolean; +} + +export interface IStartCheckoutResponse { + status: string; + message: string; + psp_response: Record; + checkout_id: string; + is_route_finished: Boolean; + transaction_status: string; + transaction_id: number; + payment_id: number; + provider: string; + next_action: { + redirect_to_url?: { + url: string; + return_url: string; + verify_transaction_status_url: string; + }; + iframe_resources?: { + iframe: string; + }; + }; + actions: IStartCheckoutActionResponse[]; +} + +export interface IStartCheckoutActionResponse { + name: string; + url: string; + method: string; +} + + +export interface IItem { + description: string; + quantity: number; + price_unit: number; + discount: number; + taxes: number; + product_reference: string | number; + name: string; + amount_total: number; +} + +export interface IProcessPaymentRequest { + customer: ICustomer; + cart: { + total: string | number; + items: IItem[]; + }; + metadata?: Record; + currency?: string; + payment_method?: string; + card?: ICardFields | string; + isSandbox?: boolean; + /** + * @deprecated This property is deprecated and will be removed in a future release. + * Use the `returnUrl` field when creating the instance of LiteCheckout or InlineCheckout. + */ + returnUrl?: string; +} + +export interface ICardFields { + card_number: string; + cvv: string; + expiration_month: string; + expiration_year: string; + cardholder_name: string; +} \ No newline at end of file diff --git a/src/types/commons.ts b/src/types/commons.ts index e1bee9a..ea6df15 100644 --- a/src/types/commons.ts +++ b/src/types/commons.ts @@ -1,83 +1,131 @@ +import { ICustomer } from "./customer"; +import {IStartCheckoutResponse} from "./checkout"; + export type Business = { - business: { - pk: number; - name: string; - categories: { - pk: number; - name: string; - }[]; - web: string; - logo: string; - full_logo_url: string; - background_color: string; - primary_color: string; - checkout_mode: boolean; - textCheckoutColor: string; - textDetailsColor: string; - checkout_logo: string; - }; - openpay_keys: { - merchant_id: string; - public_key: string; - }; - fintoc_keys: { - public_key: string; - }; - mercado_pago: { - active: boolean; - }; - vault_id: string; - vault_url: string; - reference: number; - is_installments_available: boolean; + business: { + pk: number; + name: string; + categories: { + pk: number; + name: string; + }[]; + web: string; + logo: string; + full_logo_url: string; + background_color: string; + primary_color: string; + checkout_mode: boolean; + textCheckoutColor: string; + textDetailsColor: string; + checkout_logo: string; + }; + openpay_keys: { + merchant_id: string; + public_key: string; + }; + fintoc_keys: { + public_key: string; + }; + mercado_pago: { + active: boolean; + }; + vault_id: string; + vault_url: string; + reference: number; + is_installments_available: boolean; }; export type Customer = { - firstName: string; - lastName: string; - country: string; - street: string; - city: string; - state: string; - postCode: string; - email: string; - phone: string; + firstName: string; + lastName: string; + country: string; + street: string; + city: string; + state: string; + postCode: string; + email: string; + phone: string; }; export type OrderItem = { - description: string; - quantity: number; - price_unit: number; - discount: number; - taxes: number; - product_reference: number; - name: string; - amount_total: number; + description: string; + quantity: number; + price_unit: number; + discount: number; + taxes: number; + product_reference: number; + name: string; + amount_total: number; }; export type PaymentData = { - customer: Customer; - currency: string; - cart: { - total: string | number; - items: OrderItem[]; - }; + customer: Customer; + currency: string; + cart: { + total: string | number; + items: OrderItem[]; + }; }; export type TonderAPM = { - pk: string; - payment_method: string; - priority: number; - category: string; - unavailable_countries: string[]; - status: string; -} + pk: string; + payment_method: string; + priority: number; + category: string; + unavailable_countries: string[]; + status: string; +}; export type APM = { - id: string; - payment_method: string; - priority: number; - category: string; - icon: string; - label: string; + id: string; + payment_method: string; + priority: number; + category: string; + icon: string; + label: string; +}; + +export interface IConfigureCheckout { + customer: ICustomer | { email: string }; + secureToken: string +} + +export interface IInlineCheckoutBaseOptions { + mode?: "production" | "sandbox" | "stage" | "development"; + /** + * @deprecated This property is deprecated and will be removed in a future release. + * `baseUrlTonder` is no longer required. + */ + baseUrlTonder?: string; + /** + * @deprecated This property is deprecated and will be removed in a future release. + * Use `apiKey` instead, as `apiKeyTonder` is no longer required. + */ + apiKeyTonder?: string; + /** + * @deprecated This property is deprecated and will be removed in a future release. + * `signal` is no longer required. + */ + signal?: AbortSignal; + apiKey: string; + returnUrl?: string; + callBack?: (response: IStartCheckoutResponse | Record) => void; +} + +export interface IInlineLiteCheckoutOptions + extends IInlineCheckoutBaseOptions {} + + +export interface IApiError { + code: string; + body: Record | string; + name: string; + message: string; +} + +export interface IPublicError { + status: string; + code: number; + message: string; + detail: Record | string; } diff --git a/src/types/customer.ts b/src/types/customer.ts new file mode 100644 index 0000000..b55a966 --- /dev/null +++ b/src/types/customer.ts @@ -0,0 +1,22 @@ +export type ICustomer = { + /** + * @deprecated This property is deprecated and will be removed in a future release. + * Use `firstName` instead, as `name` is no longer required. + */ + name?: string; + /** + * @deprecated This property is deprecated and will be removed in a future release. + * Use `lastName` instead, as `lastname` is no longer required. + */ + lastname?: string; + firstName: string; + lastName: string; + country?: string; + street?: string; + city?: string; + state?: string; + postCode?: string; + email: string; + phone?: string; + address?: string; +}; diff --git a/src/types/liteInlineCheckout.ts b/src/types/liteInlineCheckout.ts new file mode 100644 index 0000000..058280f --- /dev/null +++ b/src/types/liteInlineCheckout.ts @@ -0,0 +1,216 @@ +import { IConfigureCheckout, IInlineCheckoutBaseOptions } from "./commons"; +import { + ICustomerCardsResponse, + ISaveCardRequest, + ISaveCardResponse, +} from "./card"; +import { IPaymentMethod } from "./paymentMethod"; +import { IProcessPaymentRequest, IStartCheckoutResponse } from "./checkout"; +import { ITransaction } from "./transaction"; +import { APM } from "./commons"; +import { ErrorResponse } from "../classes/errorResponse"; +import { + CreateOrderRequest, + CreatePaymentRequest, + RegisterCustomerCardRequest, + StartCheckoutFullRequest, + StartCheckoutIdRequest, + StartCheckoutRequest, + TokensRequest, +} from "./requests"; +import { + CreateOrderResponse, + CreatePaymentResponse, + CustomerRegisterResponse, + GetBusinessResponse, + RegisterCustomerCardResponse, + StartCheckoutResponse, +} from "./responses"; + +export interface ILiteCheckout { + + /** + * The configureCheckout function allows you to set initial information, such as the customer's email, which is used to retrieve a list of saved cards. + * @param {import("./index").IConfigureCheckout} data - Configuration data including customer information and potentially other settings. + * @returns {Promise}. + * @public + */ + configureCheckout(data: IConfigureCheckout): void; + + /** + * Initializes and prepares the checkout for use. + * This method set up the initial environment. + * @returns {Promise} A promise that resolves when the checkout has been initialized. + * @throws {Error} If there's any problem during the checkout initialization. + * @public + */ + injectCheckout(): Promise; + + /** + * Processes a payment. + * @param {import("./index").IProcessPaymentRequest} data - Payment data including customer, cart, and other relevant information. + * @returns {Promise} A promise that resolves with the payment response or 3DS redirect or is rejected with an error. + * + * @throws {Error} Throws an error if the checkout process fails. The error object contains + * additional `details` property with the response from the server if available. + * @property {import("./index").IStartCheckoutErrorResponse} error.details - The response body from the server when an error occurs. + * + * @public + */ + payment(data: IProcessPaymentRequest): Promise; + + /** + * Verifies the 3DS transaction status. + * @returns {Promise} The result of the 3DS verification and checkout resumption. + * @public + */ + verify3dsTransaction(): Promise; + + /** + * Retrieves the list of cards associated with a customer. + * @returns {Promise} A promise that resolves with the customer's card data. + * + * @throws {import("./index").IPublicError} Throws an error object if the operation fails. + * + * @public + */ + getCustomerCards(): Promise; + + /** + * Saves a card to a customer's account. This method can be used to add a new card + * or update an existing one. + * @param {import("./index").ISaveCardRequest} card - The card information to be saved. + * @returns {Promise} A promise that resolves with the saved card data. + * + * @throws {import("./index").IPublicError} Throws an error object if the operation fails. + * + * @public + */ + saveCustomerCard(secureToken: string, card: ISaveCardRequest): Promise; + + /** + * Removes a card from a customer's account. + * @param {string} skyflowId - The unique identifier of the card to be deleted. + * @returns {Promise} A promise that resolves when the card is successfully deleted. + * + * @throws {import("./index").IPublicError} Throws an error object if the operation fails. + * + * @public + */ + removeCustomerCard(skyflowId: string): Promise; + + /** + * Retrieves the list of available Alternative Payment Methods (APMs). + * @returns {Promise} A promise that resolves with the list of APMs. + * + * @throws {import("./index").IPublicError} Throws an error object if the operation fails. + * + * @public + */ + getCustomerPaymentMethods(): Promise; + + /** + * @deprecated This method is deprecated and will be removed in a future release. + * It is no longer necessary to use this method, now automatically handled + * during the payment process or when using card management methods. + * + * Retrieves the business information. + * @returns {Promise} A promise that resolves with the business information. + * + * @throws {import("./index").IPublicError} Throws an error object if the operation fails. + * + * @public + */ + getBusiness(): Promise; + + /** + * @deprecated This method is deprecated and will be removed in a future release. + * It is no longer necessary to use this method as customer registration is now automatically handled + * during the payment process or when using card management methods. + */ + customerRegister( + email: string, + ): Promise; + + /** + * @deprecated This method is deprecated and will be removed in a future release. + * It is no longer necessary to use this method as order creation is now automatically + * handled when making a payment through the `payment` function. + */ + createOrder( + orderItems: CreateOrderRequest, + ): Promise; + + /** + * @deprecated This method is deprecated and will be removed in a future release. + * It is no longer necessary to use this method as payment creation is now automatically + * handled when making a payment through the `payment` function. + */ + createPayment( + paymentItems: CreatePaymentRequest, + ): Promise; + + /** + * @deprecated This method is deprecated and will be removed in a future release. + * Use the {@link payment} method + */ + startCheckoutRouter( + routerData: StartCheckoutRequest | StartCheckoutIdRequest, + ): Promise; + + /** + * @deprecated This method is deprecated and will be removed in a future release. + * Use the {@link payment} method + */ + startCheckoutRouterFull( + routerFullData: StartCheckoutFullRequest, + ): Promise; + + /** + * @deprecated This method is deprecated and will be removed in a future release. + * Use the {@link saveCustomerCard} method + */ + registerCustomerCard( + secureToken: string, + customerToken: string, + data: RegisterCustomerCardRequest, + ): Promise; + + /** + * @deprecated This method is deprecated and will be removed in a future release. + * Use the {@link removeCustomerCard} method + */ + deleteCustomerCard( + customerToken: string, + skyflowId: string, + ): Promise; + + /** + * @deprecated This method is deprecated and will be removed in a future release. + * Use the {@link getCustomerPaymentMethods} method + */ + getActiveAPMs(): Promise; + + /** + * @deprecated This method is deprecated and will be removed in a future release. + * It is no longer necessary to use this method as card registration or as checkout is now automatically handled + * during the payment process or when using card management methods. + */ + getSkyflowTokens({ + vault_id, + vault_url, + data, + }: TokensRequest): Promise; + + /** + * @deprecated This method is deprecated and will be removed in a future release. + * It is no longer necessary to use this method is now automatically handled + * during the payment process. + */ + getOpenpayDeviceSessionID( + merchant_id: string, + public_key: string, + is_sandbox: boolean, + ): Promise; +} + diff --git a/src/types/paymentMethod.ts b/src/types/paymentMethod.ts new file mode 100644 index 0000000..cb96ec7 --- /dev/null +++ b/src/types/paymentMethod.ts @@ -0,0 +1,24 @@ +export interface IPaymentMethodResponse { + count: number; + next: string | null; + previous: string | null; + results: ITonderPaymentMethod[]; +} + +export interface ITonderPaymentMethod { + pk: string; + payment_method: string; + priority: number; + category: string; + unavailable_countries: string[]; + status: string; +} + +export interface IPaymentMethod { + id: string; + payment_method: string; + priority: number; + category: string; + icon: string; + label: string; +} \ No newline at end of file diff --git a/src/types/requests.ts b/src/types/requests.ts index 4832aab..bad288d 100644 --- a/src/types/requests.ts +++ b/src/types/requests.ts @@ -1,5 +1,5 @@ -import { OrderItem } from "./commons"; import { SkyflowRecord } from "./skyflow"; +import {IItem} from "./checkout"; export interface CreateOrderRequest { business: string, @@ -10,7 +10,7 @@ export interface CreateOrderRequest { status?: string, reference: string | number, is_oneclick: boolean, - items: OrderItem[] + items: IItem[] } export type CreatePaymentRequest = { @@ -71,6 +71,15 @@ export type RegisterCustomerCardRequest = { skyflow_id: string; } +export type TokensSkyflowRequest = { + baseUrl: string; + apiKey: string; + vault_id: string, + vault_url: string, + data: { + [key: string]: any; + } +} export type TokensRequest = { vault_id: string, vault_url: string, @@ -81,7 +90,7 @@ export type TokensRequest = { export type StartCheckoutFullRequest = { order: { - items: OrderItem[]; + items: IItem[]; }; total: number; customer: { diff --git a/src/types/transaction.ts b/src/types/transaction.ts new file mode 100644 index 0000000..9912508 --- /dev/null +++ b/src/types/transaction.ts @@ -0,0 +1,101 @@ +export interface ITransaction { + id: number; + provider: string; + country: string; + currency_code: string; + transaction_status: string; + created: string; + modified: string; + operation_date: string; + transaction_reference: string; + transaction_type: string; + status: string; + amount: string; + related_transaction_reference?: null | string; + reason?: null | string; + is_refunded?: null | boolean; + is_disputed?: null | boolean; + number_of_payment_attempts: number; + card_brand?: null | string; + number_of_installments: number; + payment?: { + id: number; + created: string; + modified: string; + amount: string; + status: string; + date: string; + paid_date: null | string; + source: null | string; + customer_order_reference: null | string; + client: number; + business: number; + shipping_address?: null | string; + billing_address?: null | string; + order: number; + }; + checkout: { + id: string; + created: string; + modified: string; + checkout_data: { + name: string; + amount: number; + source: string; + id_ship: string; + currency: string; + order_id: number; + token_id: string; + last_name: string; + id_product: string; + ip_address: string; + payment_id: number; + return_url: string; + title_ship: string; + business_id: number; + checkout_id: string; + description: string; + browser_info: { + language: string; + time_zone: number; + user_agent: string; + color_depth: number; + screen_width: number; + screen_height: number; + javascript_enabled: boolean; + }; + email_client: string; + phone_number: string; + instance_id_ship: string; + quantity_product: number; + device_session_id: null | string; + number_of_payment_attempts: number; + }; + number_of_payment_attempts: number; + tried_psps: string[]; + rejected_transactions: string[]; + routing_step: number; + route_length: number; + last_status: string; + ip_address: string; + is_dynamic_routing: boolean; + is_route_finished: boolean; + business: number; + payment: number; + }; + currency: { + id: number; + name: string; + code: string; + symbol: string; + country: null | string; + }; + payment_method?: null | { + id: number; + name: string; + display_name: string; + category: string; + is_apm: boolean; + }; + issuing_country?: null | string; +} diff --git a/src/types/validations.d.ts b/src/types/validations.d.ts new file mode 100644 index 0000000..b0064fd --- /dev/null +++ b/src/types/validations.d.ts @@ -0,0 +1,11 @@ +export declare function validateCardNumber(cardNumber: string): boolean; + +export declare function validateCardholderName(name: string): boolean; + +export declare function validateCVV(cvv: string): boolean; + +export declare function validateExpirationMonth(month: string): boolean; + +export declare function validateExpirationYear(year: string): boolean; + +export declare function validateExpirationDateParts(month: string, year: string): boolean; diff --git a/tests/classes/liteCheckout.test.ts b/tests/classes/liteCheckout.test.ts index b900382..0731303 100644 --- a/tests/classes/liteCheckout.test.ts +++ b/tests/classes/liteCheckout.test.ts @@ -1,7 +1,7 @@ import "../utils/defaultMock"; import { LiteCheckout } from "../../src"; -import { LiteCheckoutConstructor } from "../../src/classes/liteCheckout"; import { constructorFields } from "../utils/defaultMock"; +import {IInlineLiteCheckoutOptions} from "../../src/types/commons"; declare global { interface Window { @@ -11,7 +11,7 @@ declare global { } describe("LiteCheckout", () => { - let checkoutConstructor: LiteCheckoutConstructor, + let checkoutConstructor: IInlineLiteCheckoutOptions, liteCheckout: LiteCheckout, fetchSpy: jest.SpyInstance, liteCheckoutSpy: jest.SpyInstance; @@ -38,9 +38,9 @@ describe("LiteCheckout", () => { it("Can instance LiteCheckout", () => { expect(liteCheckout).toBeInstanceOf(LiteCheckout); - expect(liteCheckout.publicApiKeyTonder).toEqual(constructorFields.publicApiKeyTonder); - expect(liteCheckout.baseUrlTonder).toEqual(constructorFields.baseUrlTonder); - expect(liteCheckout.signal).toEqual(constructorFields.signal); + expect(liteCheckout.apiKeyTonder).toEqual(constructorFields.apiKey); + expect(liteCheckout.baseUrl).toEqual(constructorFields.baseUrl); + expect(liteCheckout.abortController.signal).toEqual(constructorFields.signal); }); diff --git a/tests/methods/createOrder.test.ts b/tests/methods/createOrder.test.ts index 8f6e9ec..59a63a0 100644 --- a/tests/methods/createOrder.test.ts +++ b/tests/methods/createOrder.test.ts @@ -1,10 +1,9 @@ import "../utils/defaultMock"; import { LiteCheckout } from "../../src"; import { ErrorResponse } from "../../src/classes/errorResponse"; -import { LiteCheckoutConstructor } from "../../src/classes/liteCheckout"; -import { IErrorResponse } from "../../src/types/responses"; import { constructorFields } from "../utils/defaultMock"; -import { OrderResponseClass, OrderClass, OrderClassEmptyValues, OrderEmptyValuesResponse } from "../utils/mockClasses"; +import { OrderResponseClass, OrderClass, OrderEmptyValuesResponse } from "../utils/mockClasses"; +import {IInlineLiteCheckoutOptions} from "../../src/types/commons"; declare global { interface Window { @@ -14,7 +13,7 @@ declare global { } describe("createOrder", () => { - let checkoutConstructor: LiteCheckoutConstructor, + let checkoutConstructor: IInlineLiteCheckoutOptions, liteCheckout: LiteCheckout, fetchSpy: jest.SpyInstance, liteCheckoutSpy: jest.SpyInstance; diff --git a/tests/methods/createPayment.test.ts b/tests/methods/createPayment.test.ts index 21f7444..43fbc24 100644 --- a/tests/methods/createPayment.test.ts +++ b/tests/methods/createPayment.test.ts @@ -1,10 +1,9 @@ import "../utils/defaultMock"; import { LiteCheckout } from "../../src"; import { ErrorResponse } from "../../src/classes/errorResponse"; -import { LiteCheckoutConstructor } from "../../src/classes/liteCheckout"; -import { IErrorResponse } from "../../src/types/responses"; import { constructorFields } from "../utils/defaultMock"; import { CreatePaymentResponseClass, CreatePaymentRequestClass } from "../utils/mockClasses"; +import {IInlineLiteCheckoutOptions} from "../../src/types/commons"; declare global { @@ -15,7 +14,7 @@ declare global { } describe("createPayment", () => { - let checkoutConstructor: LiteCheckoutConstructor, + let checkoutConstructor: IInlineLiteCheckoutOptions, liteCheckout: LiteCheckout, fetchSpy: jest.SpyInstance, liteCheckoutSpy: jest.SpyInstance; diff --git a/tests/methods/customerRegister.test.ts b/tests/methods/customerRegister.test.ts index 0446f0f..e8f4feb 100644 --- a/tests/methods/customerRegister.test.ts +++ b/tests/methods/customerRegister.test.ts @@ -1,10 +1,9 @@ import "../utils/defaultMock"; import { LiteCheckout } from "../../src"; import { ErrorResponse } from "../../src/classes/errorResponse"; -import { LiteCheckoutConstructor } from "../../src/classes/liteCheckout"; -import { IErrorResponse } from "../../src/types/responses"; import { constructorFields } from "../utils/defaultMock"; -import { BusinessClass, CustomerRegisterClass } from "../utils/mockClasses"; +import { CustomerRegisterClass } from "../utils/mockClasses"; +import {IInlineLiteCheckoutOptions} from "../../src/types/commons"; declare global { @@ -15,7 +14,7 @@ declare global { } describe("customerRegister", () => { - let checkoutConstructor: LiteCheckoutConstructor, + let checkoutConstructor: IInlineLiteCheckoutOptions, liteCheckout: LiteCheckout, fetchSpy: jest.SpyInstance, liteCheckoutSpy: jest.SpyInstance; diff --git a/tests/methods/getBusiness.test.ts b/tests/methods/getBusiness.test.ts index 7112fbe..4c94b3e 100644 --- a/tests/methods/getBusiness.test.ts +++ b/tests/methods/getBusiness.test.ts @@ -1,10 +1,9 @@ import "../utils/defaultMock"; import { LiteCheckout } from "../../src"; import { ErrorResponse } from "../../src/classes/errorResponse"; -import { LiteCheckoutConstructor } from "../../src/classes/liteCheckout"; -import { IErrorResponse } from "../../src/types/responses"; import { constructorFields } from "../utils/defaultMock"; import { BusinessClass } from "../utils/mockClasses"; +import {IInlineLiteCheckoutOptions} from "../../src/types/commons"; declare global { interface Window { @@ -14,7 +13,7 @@ declare global { } describe("getBusiness", () => { - let checkoutConstructor: LiteCheckoutConstructor, + let checkoutConstructor: IInlineLiteCheckoutOptions, liteCheckout: LiteCheckout, fetchSpy: jest.SpyInstance, liteCheckoutSpy: jest.SpyInstance; @@ -84,7 +83,7 @@ describe("getBusiness", () => { let error: ErrorResponse; try { - const response = (await liteCheckout.getBusiness()) as IErrorResponse; + const response = (await liteCheckout.getBusiness()); } catch (e: any) { error = e; expect(error.code).toStrictEqual("400"); @@ -100,7 +99,7 @@ describe("getBusiness", () => { let error: ErrorResponse; try { - const response = (await liteCheckout.getBusiness()) as ErrorResponse; + const response = (await liteCheckout.getBusiness()); } catch (e: any) { error = e; expect(error.message).toStrictEqual("error"); diff --git a/tests/methods/getCustomerCards.test.ts b/tests/methods/getCustomerCards.test.ts index b02cf54..5a17acf 100644 --- a/tests/methods/getCustomerCards.test.ts +++ b/tests/methods/getCustomerCards.test.ts @@ -1,10 +1,9 @@ import "../utils/defaultMock"; import { LiteCheckout } from "../../src"; import { ErrorResponse } from "../../src/classes/errorResponse"; -import { LiteCheckoutConstructor } from "../../src/classes/liteCheckout"; -import { IErrorResponse } from "../../src/types/responses"; import { constructorFields } from "../utils/defaultMock"; import { GetCustomerCardsResponseClass } from "../utils/mockClasses"; +import {IInlineLiteCheckoutOptions} from "../../src/types/commons"; declare global { interface Window { @@ -14,7 +13,7 @@ declare global { } describe("getCustomerCards", () => { - let checkoutConstructor: LiteCheckoutConstructor, + let checkoutConstructor: IInlineLiteCheckoutOptions, liteCheckout: LiteCheckout, fetchSpy: jest.SpyInstance, liteCheckoutSpy: jest.SpyInstance; @@ -48,7 +47,7 @@ describe("getCustomerCards", () => { }) ); - const response = await liteCheckout.getCustomerCards("1234", "123456"); + const response = await liteCheckout.getCustomerCards(); expect(response).toStrictEqual({ ...new GetCustomerCardsResponseClass() }); expect(liteCheckoutSpy).toHaveBeenCalled(); @@ -65,7 +64,7 @@ describe("getCustomerCards", () => { }) ); - const response = await liteCheckout.getCustomerCards("1234", "123456"); + const response = await liteCheckout.getCustomerCards(); expect(liteCheckoutSpy).toHaveBeenCalled(); expect(liteCheckoutSpy).toHaveReturned(); expect(response).toBeUndefined(); @@ -85,10 +84,7 @@ describe("getCustomerCards", () => { let error: ErrorResponse; try { - const response = (await liteCheckout.getCustomerCards( - "1234", - "123456" - )) as IErrorResponse; + const response = (await liteCheckout.getCustomerCards()); } catch (e: any) { error = e; expect(error.code).toStrictEqual("400"); @@ -104,10 +100,7 @@ describe("getCustomerCards", () => { let error: ErrorResponse; try { - const response = (await liteCheckout.getCustomerCards( - "1234", - "123456" - )) as IErrorResponse; + const response = (await liteCheckout.getCustomerCards()); } catch (e: any) { error = e; expect(error.message).toStrictEqual("error"); diff --git a/tests/methods/getOpenpayDeviceSessionID.test.ts b/tests/methods/getOpenpayDeviceSessionID.test.ts deleted file mode 100644 index f01f89e..0000000 --- a/tests/methods/getOpenpayDeviceSessionID.test.ts +++ /dev/null @@ -1,95 +0,0 @@ -import "../utils/defaultMock"; -import { LiteCheckout } from "../../src"; -import { ErrorResponse } from "../../src/classes/errorResponse"; -import { LiteCheckoutConstructor } from "../../src/classes/liteCheckout"; -import { IErrorResponse } from "../../src/types/responses"; -import { constructorFields } from "../utils/defaultMock"; - -declare global { - interface Window { - OpenPay: any; - Skyflow: any; - } -} - -describe("getOpenpayDeviceSessionID", () => { - let checkoutConstructor: LiteCheckoutConstructor, - liteCheckout: LiteCheckout, - fetchSpy: jest.SpyInstance, - liteCheckoutSpy: jest.SpyInstance; - - beforeEach(async () => { - window.fetch = jest.fn(); - - checkoutConstructor = { - ...constructorFields, - }; - - liteCheckout = new LiteCheckout(constructorFields); - - fetchSpy = jest.spyOn(global, "fetch"); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it("getOpenpayDeviceSessionID success", async () => { - liteCheckoutSpy = jest.spyOn(liteCheckout, "getOpenpayDeviceSessionID"); - - window.OpenPay = { - setId: jest.fn(), - setApiKey: jest.fn(), - setSandboxMode: jest.fn(), - deviceData: { - setup: jest.fn().mockImplementation(() => Promise.resolve("test")), - }, - }; - - expect( - liteCheckout.getOpenpayDeviceSessionID("4321", "1234", true) - ).resolves.toBe("test"); - expect(liteCheckoutSpy).toHaveBeenCalledWith("4321", "1234", true); - }); - - it("getOpenpayDeviceSessionID empty", async () => { - liteCheckoutSpy = jest.spyOn(liteCheckout, "getOpenpayDeviceSessionID"); - - window.OpenPay = { - setId: jest.fn(), - setApiKey: jest.fn(), - setSandboxMode: jest.fn(), - deviceData: { - setup: jest.fn().mockImplementation(() => Promise.resolve()), - }, - }; - - expect( - liteCheckout.getOpenpayDeviceSessionID("", "", true) - ).resolves.toBeUndefined(); - expect(liteCheckoutSpy).toHaveBeenCalledWith("", "", true); - }); - - it("getOpenpayDeviceSessionID error", async () => { - liteCheckoutSpy = jest.spyOn(liteCheckout, "getOpenpayDeviceSessionID"); - - window.OpenPay = { - setId: jest.fn(), - setApiKey: jest.fn(), - setSandboxMode: jest.fn(), - deviceData: { - setup: jest.fn().mockRejectedValue("error"), - }, - }; - - try { - await liteCheckout.getOpenpayDeviceSessionID("", "", true); - } catch (e) { - const error: IErrorResponse = e as IErrorResponse; - expect(liteCheckoutSpy).toHaveBeenCalledWith("", "", true); - expect(liteCheckoutSpy).toHaveReturned(); - expect(error.message).toStrictEqual("error"); - expect(error).toBeInstanceOf(ErrorResponse); - } - }); -}); \ No newline at end of file diff --git a/tests/methods/getSkyflowToken.test.ts b/tests/methods/getSkyflowToken.test.ts deleted file mode 100644 index 590d211..0000000 --- a/tests/methods/getSkyflowToken.test.ts +++ /dev/null @@ -1,155 +0,0 @@ -import "../utils/defaultMock"; -import Skyflow from "skyflow-js"; -import { LiteCheckout } from "../../src"; -import { ErrorResponse } from "../../src/classes/errorResponse"; -import { LiteCheckoutConstructor } from "../../src/classes/liteCheckout"; -import { IErrorResponse } from "../../src/types/responses"; -import { constructorFields } from "../utils/defaultMock"; -import { TokensRequestClass } from "../utils/mockClasses"; - - -declare global { - interface Window { - OpenPay: any; - Skyflow: any; - } -} - -describe("getSkyflowToken", () => { - let checkoutConstructor: LiteCheckoutConstructor, - liteCheckout: LiteCheckout, - fetchSpy: jest.SpyInstance, - liteCheckoutSpy: jest.SpyInstance; - - beforeEach(async () => { - window.fetch = jest.fn(); - - checkoutConstructor = { - ...constructorFields, - }; - - liteCheckout = new LiteCheckout(constructorFields); - - fetchSpy = jest.spyOn(global, "fetch"); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it("getSkyflowTokens success", async () => { - liteCheckout.getVaultToken = jest - .fn() - .mockImplementation(() => Promise.resolve("1234")); - - liteCheckout.getFieldsPromise = jest - .fn() - .mockImplementation(() => - Promise.resolve(new Array(5).fill(Promise.resolve(true))) - ); - - liteCheckoutSpy = jest.spyOn(liteCheckout, "getSkyflowTokens"); - - const response = await liteCheckout.getSkyflowTokens({ - ...new TokensRequestClass(), - }); - expect(response).toStrictEqual("1234"); - expect(liteCheckoutSpy).toHaveBeenCalled(); - }); - - it("getSkyflowTokens empty", async () => { - liteCheckout.getVaultToken = jest - .fn() - .mockImplementation(() => Promise.resolve("")); - - jest.spyOn(Skyflow, "init").mockImplementation(jest.fn().mockImplementation(() => ({ - container: () => ({ - collect: jest.fn().mockResolvedValue(""), - }), - }))); - - liteCheckout.getFieldsPromise = jest - .fn() - .mockImplementation(() => - Promise.resolve(new Array(5).fill(Promise.resolve(true))) - ); - - liteCheckoutSpy = jest.spyOn(liteCheckout, "getSkyflowTokens"); - - let error: ErrorResponse; - - try { - const response = await liteCheckout.getSkyflowTokens({ - ...new TokensRequestClass(), - }) as IErrorResponse; - } catch (e: any) { - error = e; - expect(error).toBeInstanceOf(ErrorResponse); - expect(error.message).toStrictEqual("Por favor, verifica todos los campos de tu tarjeta"); - } - }); - - it("getSkyflowTokens error mount fields", async () => { - liteCheckout.getVaultToken = jest - .fn() - .mockImplementation(() => Promise.resolve("")); - - jest.spyOn(Skyflow, "init").mockImplementation(jest.fn().mockImplementation(() => ({ - container: () => ({ - collect: jest.fn().mockResolvedValue(""), - }), - }))); - - liteCheckout.getFieldsPromise = jest - .fn() - .mockImplementation(() => - new Array(5).fill(false) - ); - - liteCheckoutSpy = jest.spyOn(liteCheckout, "getSkyflowTokens"); - - let error: ErrorResponse; - - try { - const response = (await liteCheckout.getSkyflowTokens({ - ...new TokensRequestClass(), - })) as IErrorResponse; - } catch (e: any) { - error = e; - expect(error).toBeInstanceOf(ErrorResponse); - expect(error.message).toStrictEqual("Ocurrió un error al montar los campos de la tarjeta"); - } - }); - - it("getSkyflowTokens error collect catch", async () => { - liteCheckout.getVaultToken = jest - .fn() - .mockImplementation(() => Promise.resolve("1234")); - - jest.spyOn(Skyflow, "init").mockImplementation(jest.fn().mockImplementation(() => ({ - container: () => ({ - collect: jest.fn().mockRejectedValue("error"), - }), - }))); - - liteCheckout.getFieldsPromise = jest - .fn() - .mockImplementation(() => - Promise.resolve(new Array(5).fill(Promise.resolve(true))) - ); - - liteCheckoutSpy = jest.spyOn(liteCheckout, "getSkyflowTokens"); - - let error: ErrorResponse; - - try { - const response = (await liteCheckout.getSkyflowTokens({ - ...new TokensRequestClass(), - })) as IErrorResponse; - } catch (e: any) { - error = e; - expect(error.message).toStrictEqual("error"); - expect(error).toBeInstanceOf(ErrorResponse); - } - }); -}); \ No newline at end of file diff --git a/tests/methods/getVaultToken.test.ts b/tests/methods/getVaultToken.test.ts deleted file mode 100644 index e1899f0..0000000 --- a/tests/methods/getVaultToken.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -import "../utils/defaultMock"; -import { LiteCheckout } from "../../src"; -import { LiteCheckoutConstructor } from "../../src/classes/liteCheckout"; -import { constructorFields } from "../utils/defaultMock"; - -declare global { - interface Window { - OpenPay: any; - Skyflow: any; - } -} - -describe("getVaultToken", () => { - let checkoutConstructor: LiteCheckoutConstructor, - liteCheckout: LiteCheckout, - fetchSpy: jest.SpyInstance, - liteCheckoutSpy: jest.SpyInstance; - - beforeEach(async () => { - window.fetch = jest.fn(); - - checkoutConstructor = { - ...constructorFields, - }; - - liteCheckout = new LiteCheckout(constructorFields); - - fetchSpy = jest.spyOn(global, "fetch"); - }); - - afterEach(() => { - jest.restoreAllMocks(); - }); - - it("getVaultToken success", async () => { - liteCheckoutSpy = jest.spyOn(liteCheckout, "getVaultToken"); - - fetchSpy.mockImplementation(() => - Promise.resolve({ - json: () => Promise.resolve({ token: "1234" }), - ok: true, - }) - ); - - const response = await liteCheckout.getVaultToken(); - expect(response).toStrictEqual("1234"); - expect(liteCheckoutSpy).toHaveBeenCalled(); - }); - - it("getVaultToken empty", async () => { - liteCheckoutSpy = jest.spyOn(liteCheckout, "getVaultToken"); - - fetchSpy.mockImplementation(() => - Promise.resolve({ - json: () => Promise.resolve(), - ok: true, - }) - ); - - const response = await liteCheckout.getVaultToken(); - expect(liteCheckoutSpy).toHaveBeenCalled(); - expect(liteCheckoutSpy).toHaveReturned(); - expect(response).toBeUndefined(); - }); - - it("getVaultToken errorResponse", async () => { - liteCheckoutSpy = jest.spyOn(liteCheckout, "getVaultToken"); - - fetchSpy.mockImplementation(() => - Promise.resolve({ - json: () => Promise.resolve(), - ok: false, - status: 400, - }) - ); - - try { - await liteCheckout.getVaultToken(); - } catch (e) { - const error = e as Error; - expect(liteCheckoutSpy).toHaveBeenCalled(); - expect(liteCheckoutSpy).rejects.toThrow(); - expect(error.message).toStrictEqual( - "Failed to retrieve bearer token; HTTPCODE: 400" - ); - expect(error).toBeInstanceOf(Error); - } - }); - - it("getVaultToken errorCatch", async () => { - liteCheckoutSpy = jest.spyOn(liteCheckout, "getVaultToken"); - - fetchSpy.mockRejectedValue("error"); - - try { - await liteCheckout.getVaultToken(); - } catch (e) { - const error = e as Error; - expect(liteCheckoutSpy).toHaveBeenCalled(); - expect(liteCheckoutSpy).rejects.toThrow(); - expect(error.message).toStrictEqual( - "Failed to retrieve bearer token; error" - ); - expect(e).toBeInstanceOf(Error); - } - }); -}); \ No newline at end of file diff --git a/tests/methods/registerCustomerCard.test.ts b/tests/methods/registerCustomerCard.test.ts index e02f826..f31aef3 100644 --- a/tests/methods/registerCustomerCard.test.ts +++ b/tests/methods/registerCustomerCard.test.ts @@ -1,10 +1,10 @@ import "../utils/defaultMock"; import { LiteCheckout } from "../../src"; -import { LiteCheckoutConstructor } from "../../src/classes/liteCheckout"; import { constructorFields } from "../utils/defaultMock"; import { RegisterCustomerCardRequestClass, RegisterCustomerCardResponseClass } from "../utils/mockClasses"; import { ErrorResponse } from "../../src/classes/errorResponse"; import { IErrorResponse } from "../../src/types/responses"; +import {IInlineLiteCheckoutOptions} from "../../src/types/commons"; declare global { interface Window { @@ -14,7 +14,7 @@ declare global { } describe("registerCustomerCard", () => { - let checkoutConstructor: LiteCheckoutConstructor, + let checkoutConstructor: IInlineLiteCheckoutOptions, liteCheckout: LiteCheckout, fetchSpy: jest.SpyInstance, liteCheckoutSpy: jest.SpyInstance; diff --git a/tests/methods/startCheckoutRouter.test.ts b/tests/methods/startCheckoutRouter.test.ts index c45bc51..79ce0ec 100644 --- a/tests/methods/startCheckoutRouter.test.ts +++ b/tests/methods/startCheckoutRouter.test.ts @@ -1,10 +1,10 @@ import "../utils/defaultMock"; import { LiteCheckout } from "../../src"; import { ErrorResponse } from "../../src/classes/errorResponse"; -import { LiteCheckoutConstructor } from "../../src/classes/liteCheckout"; import { IErrorResponse } from "../../src/types/responses"; import { constructorFields } from "../utils/defaultMock"; import { StartCheckoutResponseClass, StartCheckoutRequestClass, CreatePaymentRequestClass } from "../utils/mockClasses"; +import {IInlineLiteCheckoutOptions} from "../../src/types/commons"; declare global { interface Window { @@ -14,7 +14,7 @@ declare global { } describe("startCheckoutRouter", () => { - let checkoutConstructor: LiteCheckoutConstructor, + let checkoutConstructor: IInlineLiteCheckoutOptions, liteCheckout: LiteCheckout, fetchSpy: jest.SpyInstance, liteCheckoutSpy: jest.SpyInstance; diff --git a/tests/methods/startCheckoutRouterFull.test.ts b/tests/methods/startCheckoutRouterFull.test.ts index 659e4cc..b808880 100644 --- a/tests/methods/startCheckoutRouterFull.test.ts +++ b/tests/methods/startCheckoutRouterFull.test.ts @@ -1,10 +1,10 @@ import "../utils/defaultMock"; import { LiteCheckout } from "../../src"; import { ErrorResponse } from "../../src/classes/errorResponse"; -import { LiteCheckoutConstructor } from "../../src/classes/liteCheckout"; import { IErrorResponse } from "../../src/types/responses"; import { constructorFields } from "../utils/defaultMock"; import { StartCheckoutResponseClass, StartCheckoutFullRequestClass, BusinessClass, CustomerRegisterClass, OrderResponseClass, CreatePaymentResponseClass } from "../utils/mockClasses"; +import {IInlineLiteCheckoutOptions} from "../../src/types/commons"; declare global { interface Window { @@ -14,7 +14,7 @@ declare global { } describe("startCheckoutRouterFull", () => { - let checkoutConstructor: LiteCheckoutConstructor, + let checkoutConstructor: IInlineLiteCheckoutOptions, liteCheckout: LiteCheckout, fetchSpy: jest.SpyInstance, liteCheckoutSpy: jest.SpyInstance; diff --git a/tests/utils/defaultMock.ts b/tests/utils/defaultMock.ts index f797982..db90664 100644 --- a/tests/utils/defaultMock.ts +++ b/tests/utils/defaultMock.ts @@ -16,6 +16,7 @@ const abortSignal = new AbortController().signal; export const constructorFields = { signal: abortSignal, - baseUrlTonder: "", - publicApiKeyTonder: "", + baseUrl: "", + apiKey: "", + returnUrl: "" }; \ No newline at end of file diff --git a/tests/utils/mockClasses.ts b/tests/utils/mockClasses.ts index 0e355bb..e8a9806 100644 --- a/tests/utils/mockClasses.ts +++ b/tests/utils/mockClasses.ts @@ -4,9 +4,8 @@ import { CreatePaymentRequest, RegisterCustomerCardRequest, StartCheckoutRequest, - TokensRequest, StartCheckoutFullRequest, - StartCheckoutRequestWithCard + StartCheckoutRequestWithCard, TokensSkyflowRequest } from "../../src/types/requests"; import { CreateOrderResponse, @@ -556,13 +555,17 @@ export class StartCheckoutFullRequestClass implements StartCheckoutFullRequest { } } -export class TokensRequestClass implements TokensRequest { +export class TokensRequestClass implements TokensSkyflowRequest { + baseUrl!: string; + apiKey!: string; vault_id!: string; vault_url!: string; data: { [key: string]: any } = {}; - get mockObject(): TokensRequest { + get mockObject(): TokensSkyflowRequest { return { + baseUrl: "", + apiKey: "", vault_id: "string", vault_url: "string", data: { diff --git a/types/classes/liteCheckout.d.ts b/types/classes/liteCheckout.d.ts new file mode 100644 index 0000000..e7c4818 --- /dev/null +++ b/types/classes/liteCheckout.d.ts @@ -0,0 +1,29 @@ +import { TokensRequest } from "../types/skyflow"; +declare global { + interface Window { + OpenPay: any; + } +} +type LiteCheckoutConstructor = { + signal: AbortSignal; + baseUrlTonder: string; + publicApiKeyTonder: string; +}; +export declare class LiteCheckout { + baseUrlTonder: string; + signal: AbortSignal; + publicApiKeyTonder: string; + constructor({ signal, baseUrlTonder, publicApiKeyTonder }: LiteCheckoutConstructor); + getOpenpayDeviceSessionID(merchant_id: string, public_key: string): Promise; + getBusiness(): Promise; + customerRegister(email: string): Promise; + createOrder(orderItems: any): Promise; + createPayment(paymentItems: { + business_pk: string; + }): Promise; + startCheckoutRouter(routerItems: any): Promise; + getSkyflowTokens({ vault_id, vault_url, data }: TokensRequest): Promise; + getVaultToken(): Promise; + getCustomerCards(customerToken: string, query: string): Promise; +} +export {}; diff --git a/types/classes/liteCheckout.js b/types/classes/liteCheckout.js new file mode 100644 index 0000000..77eb87c --- /dev/null +++ b/types/classes/liteCheckout.js @@ -0,0 +1,225 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LiteCheckout = void 0; +const skyflow_js_1 = __importDefault(require("skyflow-js")); +class LiteCheckout { + constructor({ signal, baseUrlTonder, publicApiKeyTonder }) { + this.baseUrlTonder = baseUrlTonder; + this.signal = signal; + this.publicApiKeyTonder = publicApiKeyTonder; + } + getOpenpayDeviceSessionID(merchant_id, public_key) { + return __awaiter(this, void 0, void 0, function* () { + let openpay = yield window.OpenPay; + openpay.setId(merchant_id); + openpay.setApiKey(public_key); + openpay.setSandboxMode(true); + var response = yield openpay.deviceData.setup({ signal: this.signal }); + return response; + }); + } + getBusiness() { + return __awaiter(this, void 0, void 0, function* () { + const getBusiness = yield fetch(`${this.baseUrlTonder}/api/v1/payments/business/${this.publicApiKeyTonder}`, { + headers: { + Authorization: `Token ${this.publicApiKeyTonder}`, + }, + signal: this.signal, + }); + const response = yield getBusiness.json(); + return response; + }); + } + customerRegister(email) { + return __awaiter(this, void 0, void 0, function* () { + const url = `${this.baseUrlTonder}/api/v1/customer/`; + const data = { email: email }; + const response = yield fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${this.publicApiKeyTonder}`, + }, + signal: this.signal, + body: JSON.stringify(data), + }); + if (response.status === 201) { + const jsonResponse = yield response.json(); + return jsonResponse; + } + else { + throw new Error(`Error: ${response.statusText}`); + } + }); + } + createOrder(orderItems) { + return __awaiter(this, void 0, void 0, function* () { + const url = `${this.baseUrlTonder}/api/v1/orders/`; + const data = orderItems; + const response = yield fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${this.publicApiKeyTonder}`, + }, + body: JSON.stringify(data), + }); + if (response.status === 201) { + const jsonResponse = yield response.json(); + return jsonResponse; + } + else { + throw new Error(`Error: ${response.statusText}`); + } + }); + } + createPayment(paymentItems) { + return __awaiter(this, void 0, void 0, function* () { + const url = `${this.baseUrlTonder}/api/v1/business/${paymentItems.business_pk}/payments/`; + const data = paymentItems; + const response = yield fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${this.publicApiKeyTonder}`, + }, + body: JSON.stringify(data), + }); + if (response.status >= 200 && response.status <= 299) { + const jsonResponse = yield response.json(); + return jsonResponse; + } + else { + throw new Error(`Error: ${response.statusText}`); + } + }); + } + startCheckoutRouter(routerItems) { + return __awaiter(this, void 0, void 0, function* () { + try { + const url = `${this.baseUrlTonder}/api/v1/checkout-router/`; + const data = routerItems; + const response = yield fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Token ${this.publicApiKeyTonder}`, + }, + body: JSON.stringify(data), + }); + if (response.status >= 200 && response.status <= 299) { + const jsonResponse = yield response.json(); + return jsonResponse; + } + else { + throw new Error("Failed to start checkout router"); + } + } + catch (error) { + throw error; + } + }); + } + getSkyflowTokens({ vault_id, vault_url, data }) { + return __awaiter(this, void 0, void 0, function* () { + const skyflow = skyflow_js_1.default.init({ + vaultID: vault_id, + vaultURL: vault_url, + getBearerToken: () => __awaiter(this, void 0, void 0, function* () { return yield this.getVaultToken(); }), + options: { + logLevel: skyflow_js_1.default.LogLevel.ERROR, + env: skyflow_js_1.default.Env.DEV, + }, + }); + const collectContainer = skyflow.container(skyflow_js_1.default.ContainerType.COLLECT); + const fields = yield Promise.all(Object.keys(data).map((key) => __awaiter(this, void 0, void 0, function* () { + const cardHolderNameElement = yield collectContainer.create({ + table: "cards", + column: key, + type: skyflow_js_1.default.ElementType.INPUT_FIELD + }); + return { element: cardHolderNameElement, key: key }; + }))); + const fieldPromises = fields.map((field) => { + return new Promise((resolve, reject) => { + var _a; + const div = document.createElement("div"); + div.hidden = true; + div.id = `id-${field.key}`; + (_a = document.querySelector(`body`)) === null || _a === void 0 ? void 0 : _a.appendChild(div); + setTimeout(() => { + field.element.mount(`#id-${field.key}`); + setInterval(() => { + if (field.element.isMounted()) { + const value = data[field.key]; + field.element.update({ value: value }); + return resolve(field.element.isMounted()); + } + }, 120); + }, 120); + }); + }); + const result = yield Promise.all(fieldPromises); + const mountFail = result.find((item) => !item); + if (mountFail) { + return { error: "Ocurrió un error al montar los campos de la tarjeta" }; + } + else { + try { + const collectResponseSkyflowTonder = yield collectContainer.collect(); + return collectResponseSkyflowTonder["records"][0]["fields"]; + } + catch (error) { + console.error("Por favor, verifica todos los campos de tu tarjeta"); + return { error: "Por favor, verifica todos los campos de tu tarjeta" }; + } + } + }); + } + getVaultToken() { + return __awaiter(this, void 0, void 0, function* () { + const response = yield fetch(`${this.baseUrlTonder}/api/v1/vault-token/`, { + method: 'GET', + headers: { + 'Authorization': `Token ${this.publicApiKeyTonder}` + }, + signal: this.signal, + }); + if (response.ok) { + const responseBody = yield response.json(); + return responseBody.token; + } + else { + throw new Error('Failed to retrieve bearer token'); + } + }); + } + getCustomerCards(customerToken, query) { + return __awaiter(this, void 0, void 0, function* () { + const response = yield fetch(`${this.baseUrlTonder}/api/v1/cards/${query}`, { + method: 'GET', + headers: { + 'Authorization': `Token ${customerToken}` + }, + signal: this.signal, + }); + const jsonResponse = yield response.json(); + console.log("jsonResponse: ", jsonResponse); + return jsonResponse; + }); + } +} +exports.LiteCheckout = LiteCheckout; +//# sourceMappingURL=liteCheckout.js.map \ No newline at end of file diff --git a/types/classes/liteCheckout.js.map b/types/classes/liteCheckout.js.map new file mode 100644 index 0000000..65a79de --- /dev/null +++ b/types/classes/liteCheckout.js.map @@ -0,0 +1 @@ +{"version":3,"file":"liteCheckout.js","sourceRoot":"","sources":["../../src/classes/liteCheckout.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AACA,4DAAiC;AAYjC,MAAa,YAAY;IAMvB,YAAa,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAA2B;QAC3E,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QACnC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;IAEK,yBAAyB,CAAC,WAAmB,EAAE,UAAkB;;YACrE,IAAI,OAAO,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;YACnC,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAC3B,OAAO,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;YAC9B,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,QAAQ,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACvE,OAAO,QAAQ,CAAC;QAClB,CAAC;KAAA;IACK,WAAW;;YACf,MAAM,WAAW,GAAG,MAAM,KAAK,CAC7B,GAAG,IAAI,CAAC,aAAa,6BAA6B,IAAI,CAAC,YAAY,EAAE,EACrE;gBACE,OAAO,EAAE;oBACP,aAAa,EAAE,SAAS,IAAI,CAAC,YAAY,EAAE;iBAC5C;gBACD,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CACF,CAAC;YACF,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,CAAC;YAC1C,OAAO,QAAQ,CAAA;QACjB,CAAC;KAAA;IACK,gBAAgB,CAAC,KAAa;;YAClC,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,aAAa,mBAAmB,CAAC;YACrD,MAAM,IAAI,GAAG,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;YAC9B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,aAAa,EAAE,SAAS,IAAI,CAAC,YAAY,EAAE;iBAC5C;gBACD,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;aAC3B,CAAC,CAAC;YAEH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAC3C,OAAO,YAAY,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,KAAK,CAAC,UAAU,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;KAAA;IAEK,WAAW,CAAC,UAAe;;YAC/B,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,aAAa,iBAAiB,CAAC;YACnD,MAAM,IAAI,GAAG,UAAU,CAAC;YACxB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,aAAa,EAAE,SAAS,IAAI,CAAC,YAAY,EAAE;iBAC5C;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;aAC3B,CAAC,CAAC;YACH,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAC3C,OAAO,YAAY,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,KAAK,CAAC,UAAU,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;KAAA;IAEK,aAAa,CAAC,YAAqC;;YACvD,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,aAAa,oBAAoB,YAAY,CAAC,WAAW,YAAY,CAAC;YAC1F,MAAM,IAAI,GAAG,YAAY,CAAC;YAC1B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,aAAa,EAAE,SAAS,IAAI,CAAC,YAAY,EAAE;iBAC5C;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;aAC3B,CAAC,CAAC;YACH,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAG,GAAG,EAAE,CAAC;gBACpD,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAC3C,OAAO,YAAY,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,KAAK,CAAC,UAAU,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YACnD,CAAC;QACH,CAAC;KAAA;IAEK,mBAAmB,CAAC,WAAgB;;YACxC,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,aAAa,0BAA0B,CAAC;gBAC5D,MAAM,IAAI,GAAG,WAAW,CAAC;gBACzB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;oBAChC,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE;wBACP,cAAc,EAAE,kBAAkB;wBAClC,aAAa,EAAE,SAAS,IAAI,CAAC,YAAY,EAAE;qBAC5C;oBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;iBAC3B,CAAC,CAAC;gBACH,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;oBACrD,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;oBAC3C,OAAO,YAAY,CAAC;gBACtB,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAA;gBACpD,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,KAAK,CAAA;YACb,CAAC;QACH,CAAC;KAAA;IAEK,gBAAgB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAiB;;YAEjE,MAAM,OAAO,GAAG,oBAAO,CAAC,IAAI,CAAC;gBAC3B,OAAO,EAAE,QAAQ;gBACjB,QAAQ,EAAE,SAAS;gBACnB,cAAc,EAAE,GAAS,EAAE,gDAAC,OAAA,MAAM,IAAI,CAAC,aAAa,EAAE,CAAA,GAAA;gBACtD,OAAO,EAAE;oBACP,QAAQ,EAAE,oBAAO,CAAC,QAAQ,CAAC,KAAK;oBAChC,GAAG,EAAE,oBAAO,CAAC,GAAG,CAAC,GAAG;iBACrB;aACF,CAAC,CAAC;YAEH,MAAM,gBAAgB,GAAQ,OAAO,CAAC,SAAS,CAC7C,oBAAO,CAAC,aAAa,CAAC,OAAO,CAC9B,CAAC;YAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAO,GAAG,EAAE,EAAE;gBACnE,MAAM,qBAAqB,GAAG,MAAM,gBAAgB,CAAC,MAAM,CAAC;oBAC1D,KAAK,EAAE,OAAO;oBACd,MAAM,EAAE,GAAG;oBACX,IAAI,EAAE,oBAAO,CAAC,WAAW,CAAC,WAAW;iBACtC,CAAC,CAAC;gBACH,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,GAAG,EAAE,GAAG,EAAC,CAAC;YACrD,CAAC,CAAA,CAAC,CAAC,CAAA;YAEH,MAAM,aAAa,GAAmB,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;gBACzD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;;oBACrC,MAAM,GAAG,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;oBACzC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC;oBAClB,GAAG,CAAC,EAAE,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,CAAA;oBAC1B,MAAA,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,0CAAE,WAAW,CAAC,GAAG,CAAC,CAAC;oBACjD,UAAU,CAAC,GAAG,EAAE;wBACd,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC,GAAG,EAAE,CAAC,CAAA;wBACvC,WAAW,CAAC,GAAG,EAAE;4BACf,IAAG,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC;gCAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gCAC9B,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;gCACvC,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;4BAC3C,CAAC;wBACH,CAAC,EAAE,GAAG,CAAC,CAAA;oBACT,CAAC,EAAE,GAAG,CAAC,CAAA;gBACT,CAAC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;YAGF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;YAE/C,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAa,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,CAAA;YAEvD,IAAG,SAAS,EAAE,CAAC;gBACb,OAAO,EAAE,KAAK,EAAE,qDAAqD,EAAE,CAAA;YACzE,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC;oBACH,MAAM,4BAA4B,GAAG,MAAM,gBAAgB,CAAC,OAAO,EAAE,CAAC;oBACtE,OAAO,4BAA4B,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;gBAC9D,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,KAAK,CAAC,oDAAoD,CAAC,CAAA;oBACnE,OAAO,EAAE,KAAK,EAAE,oDAAoD,EAAE,CAAA;gBACxE,CAAC;YACH,CAAC;QAEH,CAAC;KAAA;IAEK,aAAa;;YACjB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,aAAa,sBAAsB,EAAE;gBACxE,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE;oBACP,eAAe,EAAE,SAAS,IAAI,CAAC,YAAY,EAAE;iBAC9C;gBACD,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAC,CAAC;YAEH,IAAI,QAAQ,CAAC,EAAE,EAAE,CAAC;gBAChB,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAC3C,OAAO,YAAY,CAAC,KAAK,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;KAAA;IAEK,gBAAgB,CAAE,aAAqB,EAAE,KAAa;;YAC1D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,aAAa,iBAAiB,KAAK,EAAE,EAAE;gBAC1E,MAAM,EAAE,KAAK;gBACb,OAAO,EAAE;oBACP,eAAe,EAAE,SAAS,aAAa,EAAE;iBAC1C;gBACD,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAC,CAAC;YAEH,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAC3C,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC;YAC5C,OAAO,YAAY,CAAC;QACtB,CAAC;KAAA;CAEF;AAjND,oCAiNC"} \ No newline at end of file diff --git a/types/helpers/utils.d.ts b/types/helpers/utils.d.ts new file mode 100644 index 0000000..6cb7417 --- /dev/null +++ b/types/helpers/utils.d.ts @@ -0,0 +1,3 @@ +export declare const createObserver: ({ target }: { + target: string; +}) => Promise; diff --git a/types/helpers/utils.js b/types/helpers/utils.js new file mode 100644 index 0000000..2daacf1 --- /dev/null +++ b/types/helpers/utils.js @@ -0,0 +1,27 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.createObserver = void 0; +const createObserver = ({ target }) => { + return new Promise((resolve, reject) => { + let hasChanged = false; + const targetNode = document.querySelector(target); + const config = { attributes: true, childList: true, subtree: true }; + const callback = (mutationList, observer) => { + for (const mutation of mutationList) { + if (mutation.type === "childList") { + hasChanged = true; + resolve(mutation); + } + } + }; + const observer = new MutationObserver(callback); + observer.observe(targetNode, config); + window.setTimeout(() => { + if (!hasChanged) { + reject("Mounting error"); + } + }, 5000); + }); +}; +exports.createObserver = createObserver; +//# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/types/helpers/utils.js.map b/types/helpers/utils.js.map new file mode 100644 index 0000000..d26367e --- /dev/null +++ b/types/helpers/utils.js.map @@ -0,0 +1 @@ +{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/helpers/utils.ts"],"names":[],"mappings":";;;AAAO,MAAM,cAAc,GAAG,CAAC,EAAE,MAAM,EAAsB,EAAgB,EAAE;IAE7E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAErC,IAAI,UAAU,GAAG,KAAK,CAAC;QAGvB,MAAM,UAAU,GAAQ,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QAGvD,MAAM,MAAM,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAGpE,MAAM,QAAQ,GAAG,CAAC,YAAiB,EAAE,QAA0B,EAAE,EAAE;YACjE,KAAK,MAAM,QAAQ,IAAI,YAAY,EAAE,CAAC;gBACpC,IAAI,QAAQ,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;oBAClC,UAAU,GAAG,IAAI,CAAC;oBAClB,OAAO,CAAC,QAAQ,CAAC,CAAA;gBACnB,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAGF,MAAM,QAAQ,GAAG,IAAI,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QAGhD,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAErC,MAAM,CAAC,UAAU,CAAC,GAAE,EAAE;YACpB,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM,CAAC,gBAAgB,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC,EAAE,IAAI,CAAC,CAAC;IAEX,CAAC,CAAC,CAAA;AAEJ,CAAC,CAAA;AApCY,QAAA,cAAc,kBAoC1B"} \ No newline at end of file diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 0000000..b31dbf3 --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,2 @@ +import { LiteCheckout } from './classes/liteCheckout'; +export { LiteCheckout }; diff --git a/types/index.js b/types/index.js new file mode 100644 index 0000000..e008097 --- /dev/null +++ b/types/index.js @@ -0,0 +1,6 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.LiteCheckout = void 0; +const liteCheckout_1 = require("./classes/liteCheckout"); +Object.defineProperty(exports, "LiteCheckout", { enumerable: true, get: function () { return liteCheckout_1.LiteCheckout; } }); +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/types/index.js.map b/types/index.js.map new file mode 100644 index 0000000..a0049fd --- /dev/null +++ b/types/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yDAAqD;AAGjD,6FAHK,2BAAY,OAGL"} \ No newline at end of file