webservice/Endpoint.js

/**
 * @module webservice
 */

import Rest from "./Rest.js"

/**
 * The Endpoint class is a static class that is used to set the endpoints of the web services.
 * Here you can set mode for test or deployment, and set URLs for each service.
 * @class Endpoint
 * @alias module:webservice.Endpoint
 */
class Endpoint {
	// true for deployment; false for test
	static #mode = false

	static #token

	static #offset = 4

	static INDEX_INVOICE = 0
	static INDEX_RETENTION = 1

	static INDEX_TOKEN = 1 << Endpoint.#offset
	static INDEX_SEND = 2 << Endpoint.#offset
	static INDEX_STATUS = 3 << Endpoint.#offset
	static #REST_MASK = 0b111 << Endpoint.#offset // despatch is here

	static #fetchFunction = fetch // use the default or standard JavaScript fetch

	static setFetchFunction(customFetch) {
		Endpoint.#fetchFunction = customFetch
	}

	/**
	 * Sets the deployment mode.
	 * @deprecated Use {@link Endpoint.setDeploymentMode} instead.
	 * @param {boolean} mode - The new mode to set.
	 */
	static setMode(mode) {
		Endpoint.#mode = mode
	}

	/**
	 * Changes to deployment mode because always test mode starts.
	 * @param {boolean} [mode=true] - The new mode to set. Set to `false` for test mode.
	 */
	static setDeploymentMode(mode = true) {
		Endpoint.#mode = mode
	}

	static getDeploymentMode() {
		return Endpoint.#mode
	}

	/**
	 * @param service is index integer.
	 * @param url is a string for that endpoint.
	 * @param deploymentMode is boolean with true. False to test.
	 */
	static setUrl(service, url, deploymentMode = false) {
		// setting classic services
		if (service == Endpoint.INDEX_INVOICE || service == Endpoint.INDEX_RETENTION) {
			if (deploymentMode) {
				Endpoint.#urls[service].deploy = url
			}
			else {
				Endpoint.#urls[service].test = url
			}

			return // nothing else
		}

		// setting "new" services
		if ((service & Endpoint.#REST_MASK) != 0) {
			service >>= Endpoint.#offset
			--service
			if (deploymentMode) {
				Endpoint.#restUrls[service].deploy = url
			}
			else {
				Endpoint.#restUrls[service].test = url
			}
		}
	}

	static #urls = [
		{ // invoice
			deploy: "https://e-factura.sunat.gob.pe/ol-ti-itcpfegem/billService",
			test: "https://e-beta.sunat.gob.pe/ol-ti-itcpfegem-beta/billService"
		},
		{ // retention
			deploy: "https://e-factura.sunat.gob.pe/ol-ti-itemision-otroscpe-gem/billService",
			test: "https://e-beta.sunat.gob.pe/ol-ti-itemision-otroscpe-gem-beta/billService"
		}
	]

	/**
	 * Currently for despatchs but looks like for any document.
	 */
	static #restUrls = [
		{ // despatch token
			deploy: "https://api-seguridad.sunat.gob.pe/v1/clientessol/<client_id>/oauth2/token",
			test: "https://gre-test.nubefact.com/v1/clientessol/<client_id>/oauth2/token"
		},
		{ // despatch send
			deploy: "https://api-cpe.sunat.gob.pe/v1/contribuyente/gem/comprobantes/", // ending: {numRucEmisor}-{codCpe}-{numSerie}-{numCpe}
			test: "https://gre-test.nubefact.com/v1/contribuyente/gem/comprobantes/"
		},
		{ // despatch status
			deploy: "https://api-cpe.sunat.gob.pe/v1/contribuyente/gem/comprobantes/envios/", // ending: {numTicket}
			test: "https://gre-test.nubefact.com/v1/contribuyente/gem/comprobantes/envios/"
		},
	]

	static getUrl(service, mode) {
		// Use what is in current scope
		if (mode == undefined) {
			mode = Endpoint.#mode
		}

		if (service == Endpoint.INDEX_INVOICE || service == Endpoint.INDEX_RETENTION) {
			return mode ? Endpoint.#urls[service].deploy : Endpoint.#urls[service].test
		}

		if ((service & Endpoint.#REST_MASK) != 0) {
			service >>= Endpoint.#offset
			--service
			return mode ? Endpoint.#restUrls[service].deploy : Endpoint.#restUrls[service].test
		}
	}

	static async fetch(service, body) {
		const url = Endpoint.getUrl(service)

		const response = await Endpoint.#fetchFunction(url, {
			method: "POST",
			headers: { "Content-Type": "text/xml;charset=UTF-8" },
			body: body
		})
		const responseText = await response.text()

		return responseText
	}

	static async fetchStatus(ticket) {
		const url = Endpoint.getUrl(Endpoint.INDEX_STATUS).concat(ticket)

		const response = await Endpoint.#fetchFunction(url, {
			method: "GET",
			headers: { "Content-Type": "application/json", "Authorization": "Bearer " + Endpoint.#token }
		})
		const responseJson = await response.json()

		return responseJson
	}

	static async fetchSend(body, receipt) {
		const url = Endpoint.getUrl(Endpoint.INDEX_SEND).concat(`${receipt.getTaxpayer().getIdentification().getNumber()}-${receipt.getId(true)}`)

		const response = await Endpoint.#fetchFunction(url, {
			method: "POST",
			headers: { "Content-Type": "application/json", "Authorization": "Bearer " + Endpoint.#token },
			body: body
		})
		const responseJson = await response.json()

		return responseJson
	}

	static async fetchToken(taxpayer) {
		const url = Endpoint.getUrl(Endpoint.INDEX_TOKEN).replace("<client_id>", taxpayer.getSolId())

		const data = Rest.generateToken(taxpayer)

		const response = await Endpoint.#fetchFunction(url, {
			method: "POST",
			headers: { "Content-Type": "application/x-www-form-urlencoded" },
			body: data
		})
		const responseJson = await response.json()

		// Hold value
		Endpoint.#token = responseJson.access_token

		return responseJson
	}

	static get token() {
		return Endpoint.#token
	}

	static set token(token) {
		Endpoint.#token = token
	}
}

export default Endpoint