@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
text/typescript
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);
});
}
}