UNPKG

@test-org122/merchant-iframe

Version:

Mediator iFrame package. This is used to sandbox the connector code away from the running hypernet core.

86 lines (70 loc) 3.61 kB
import { errAsync, okAsync, ResultAsync } from "neverthrow"; import { ResultUtils } from "@test-org122/utils"; import { ethers } from "ethers"; import { IMerchantConnectorRepository } from "@merchant-iframe/interfaces/data"; import { MerchantConnectorError, MerchantValidationError } from "@merchant-iframe/interfaces/objects/errors"; import { IContextProvider } from "@merchant-iframe/interfaces/utils"; import { IMerchantService } from "@merchant-iframe/interfaces/business"; import { IMerchantConnector } from "@test-org122/merchant-connector"; declare global { interface Window { connector: IMerchantConnector; } } export class MerchantService implements IMerchantService { constructor( protected merchantConnectorRepository: IMerchantConnectorRepository, protected contextProvider: IContextProvider, ) {} public activateMerchantConnector(): ResultAsync< IMerchantConnector, MerchantConnectorError | MerchantValidationError > { const context = this.contextProvider.getMerchantContext(); console.log("activateMerchantConnector"); // If we don't have validated code, that's a problem. if (context.validatedMerchantCode == null) { return errAsync(new MerchantValidationError("Cannot activate merchant connector, no validated code available!")); } // Now we eval the connector code, which is supposed to return an IMerchantConnector //const merchantConnector = eval(context.validatedMerchantCode) as IMerchantConnector; var newScript = document.createElement("script"); var inlineScript = document.createTextNode(context.validatedMerchantCode); newScript.appendChild(inlineScript); document.head.appendChild(newScript); const merchantConnector = window.connector; console.log(merchantConnector); if (merchantConnector == null) { return errAsync(new MerchantConnectorError("Validated code does not evaluate to an object")); } context.merchantConnector = merchantConnector; this.contextProvider.setMerchantContext(context); context.onMerchantConnectorActivated.next(merchantConnector); return okAsync(merchantConnector); } public validateMerchantConnector(): ResultAsync<string, MerchantValidationError> { // This is going to connect to the merchantUrl/connector and pull down the connector code. // That code is expected to be signed, with the public key available at merchantUrl/publicKey // The code will be cached in local storage but the signing key will be const context = this.contextProvider.getMerchantContext(); return ResultUtils.combine([ this.merchantConnectorRepository.getMerchantCode(context.merchantUrl), this.merchantConnectorRepository.getMerchantSignature(context.merchantUrl), this.merchantConnectorRepository.getMerchantAddress(context.merchantUrl), ]).andThen((vals) => { const [merchantCode, signature, address] = vals; const calculatedAddress = ethers.utils.verifyMessage(merchantCode, signature); if (calculatedAddress !== address) { return errAsync(new MerchantValidationError("Merchant code does not match signature!")); } // Merchant's code passes muster. Store the merchant code in the context as validated. console.log("Connector validated!"); const context = this.contextProvider.getMerchantContext(); context.validatedMerchantCode = merchantCode; context.validatedMerchantSignature = signature; this.contextProvider.setMerchantContext(context); // Return the valid signature return okAsync(signature); }); } }