/ src / api / tokenize.ts
tokenize.ts
  1  import type { Request } from "@literate.ink/utilities";
  2  import type { Configuration, Identification, Profile } from "~/models";
  3  import { defaultFetcher, type Fetcher, findValueBetween, getHeaderFromResponse } from "@literate.ink/utilities";
  4  
  5  import { XMLParser } from "fast-xml-parser";
  6  import { CLIENT_TYPE, SERVICE_VERSION, SOAP_URL, SOAP_USER_AGENT } from "~/core/constants";
  7  
  8  import { xml } from "~/core/xml";
  9  import { decodeBalance } from "~/decoders/balance";
 10  // import { setDeviceToken } from "./private/set-device-token";
 11  
 12  export const extractActivationURL = async (url: string, fetcher: Fetcher = defaultFetcher): Promise<string> => {
 13    const response = await fetcher({ redirect: "manual", url: new URL(url) });
 14    const location = getHeaderFromResponse(response, "Location");
 15  
 16    if (!location) {
 17      throw new Error("URL to tokenize expired");
 18    }
 19  
 20    return location;
 21  };
 22  
 23  // eslint-disable-next-line ts/explicit-function-return-type
 24  export const tokenize = async (url: string, fetcher: Fetcher = defaultFetcher) => {
 25    // encoded like this:
 26    // izly://SBSCR/<identifier>/<code>
 27    const parts = url.split("/");
 28    const code = parts.pop()!;
 29    const identifier = parts.pop()!;
 30  
 31    const body = xml.header + xml.envelope(`
 32      <Logon xmlns="Service" id="o0" c:root="1">
 33      ${xml.property("version", SERVICE_VERSION)}
 34      ${xml.property("channel", "AIZ")}
 35      ${xml.property("format", "T")}
 36      ${xml.property("model", "A")}
 37      ${xml.property("language", "fr")}
 38      ${xml.property("user", identifier)}
 39      <password i:null="true" />
 40      ${xml.property("smoneyClientType", CLIENT_TYPE)}
 41      ${xml.property("rooted", "0")}
 42      ${xml.property("actCode", code)}
 43      </Logon>
 44    `);
 45  
 46    const request: Request = {
 47      content: body,
 48      headers: {
 49        "clientVersion": SERVICE_VERSION,
 50        "Content-Type": "text/xml;charset=utf-8",
 51        "smoneyClientType": CLIENT_TYPE,
 52        "SOAPAction": "Service/Logon",
 53        "User-Agent": SOAP_USER_AGENT
 54      },
 55      method: "POST",
 56      url: SOAP_URL
 57    };
 58  
 59    const response = await fetcher(request);
 60  
 61    const result = findValueBetween(response.content, "<LogonResult>", "</LogonResult>");
 62    if (!result) throw new Error("No <LogonResult> found in response");
 63  
 64    const decoded = xml.from_entities(result);
 65    const parser = new XMLParser({
 66      numberParseOptions: {
 67        hex: true,
 68        leadingZeros: true,
 69        skipLike: /[0-9]/
 70      }
 71    });
 72    const { Logon } = parser.parse(decoded);
 73  
 74    const output = {
 75      balance: decodeBalance(Logon.UP),
 76  
 77      configuration: {
 78        currency: Logon.CUR,
 79        moneyInMaximum: parseFloat(Logon.MONEYINMAX),
 80        moneyInMinimum: parseFloat(Logon.MONEYINMIN),
 81        moneyOutMaximum: parseFloat(Logon.MONEYOUTMAX),
 82        moneyOutMinimum: parseFloat(Logon.MONEYOUTMIN),
 83  
 84        paymentMaximum: parseFloat(Logon.P2PPAYMAX),
 85        paymentMinimum: parseFloat(Logon.P2PPAYMIN),
 86        paymentPartMaximum: parseFloat(Logon.P2PPAYPARTMAX),
 87        paymentPartMinimum: parseFloat(Logon.P2PPAYPARTMIN)
 88      } as Configuration,
 89  
 90      identification: {
 91        accessToken: Logon.OAUTH.ACCESS_TOKEN,
 92  
 93        accessTokenExpiresIn: parseInt(Logon.OAUTH.EXPIRES_IN),
 94        counter: 0,
 95        identifier: Logon.UID,
 96        nsse: Logon.NSSE,
 97  
 98        qrCodePrivateKey: Logon.QR_CODE_PRIVATE_KEY,
 99  
100        refreshToken: Logon.OAUTH.REFRESH_TOKEN,
101        seed: Logon.SEED,
102  
103        sessionID: Logon.SID,
104        token: Logon.TOKEN,
105        userID: Logon.USER_ID,
106  
107        userPublicID: Logon.USER_PUBLIC_ID
108      } as Identification,
109  
110      profile: {
111        email: Logon.EMAIL,
112        firstName: Logon.FNAME,
113        identifier: Logon.ALIAS,
114        lastName: Logon.LNAME
115      } as Profile
116    };
117  
118    // register the token for this session id
119    // EDIT: apparently only for GCM (Google Cloud Messaging)
120    // await setDeviceToken(output.identification, fetcher);
121  
122    return output;
123  };