Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
#### PRODUCTION
# VITE_IFRAME_URL=https://iframe.string-api.xyz
# VITE_API_URL=https://api.string-api.xyz
PROD_IFRAME_URL=https://iframe.string-api.xyz
PROD_API_URL=https://api.string-api.xyz

#### DEVELOPMENT
# VITE_IFRAME_URL=https://iframe-app.dev.string-api.xyz
# VITE_API_URL=https://string-api.dev.string-api.xyz
DEV_IFRAME_URL=https://iframe-app.dev.string-api.xyz
DEV_API_URL=https://string-api.dev.string-api.xyz

#### SANDBOX
# VITE_IFRAME_URL=https://iframe-app.dev.string-api.xyz
# VITE_API_URL=https://api.sandbox.string-api.xyz
SBOX_IFRAME_URL=https://iframe-app.dev.string-api.xyz
SBOX_API_URL=https://api.sandbox.string-api.xyz

#### LOCAL
VITE_IFRAME_URL=http://localhost:4040
VITE_API_URL=http://localhost:5555
LOCAL_IFRAME_URL=http://localhost:4040
LOCAL_API_URL=http://localhost:5555

VITE_STRING_API_KEY=str...

#### ANALYTICS
VITE_ANALYTICS_LIB_PK=
VITE_ANALYTICS_SUBDOMAIN_URL=https://metrics.string.xyz
VITE_ANALYTICS_SUBDOMAIN_URL=https://metrics.string.xyz

VITE_STRING_API_KEY=str...
32 changes: 28 additions & 4 deletions rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,29 @@ config();

const version = process.env.npm_package_version

if (!process.env.VITE_IFRAME_URL || !process.env.VITE_API_URL || !process.env.VITE_ANALYTICS_LIB_PK) {
throw Error("Missing variables in .env")
const ENV_TABLE = [
{ name: 'PROD_IFRAME_URL', value: process.env.PROD_IFRAME_URL },
{ name: 'PROD_API_URL', value: process.env.PROD_API_URL },
{ name: 'SBOX_IFRAME_URL', value: process.env.SBOX_IFRAME_URL },
{ name: 'SBOX_API_URL', value: process.env.SBOX_API_URL },
{ name: 'DEV_IFRAME_URL', value: process.env.DEV_IFRAME_URL },
{ name: 'DEV_API_URL', value: process.env.DEV_API_URL },
{ name: 'LOCAL_IFRAME_URL', value: process.env.LOCAL_IFRAME_URL },
{ name: 'LOCAL_API_URL', value: process.env.LOCAL_API_URL },
]

for (const env of ENV_TABLE) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice

if (!env.value) {
throw Error(`Missing ${env.name} in .env`)
}
}

if (!process.env.VITE_ANALYTICS_LIB_PK) {
throw Error(`Missing VITE_ANALYTICS_LIB_PK in .env`)
}

if (!process.env.VITE_ANALYTICS_SUBDOMAIN_URL) {
throw Error(`Missing VITE_ANALYTICS_SUBDOMAIN_URL in .env`)
}

export default {
Expand All @@ -30,9 +51,12 @@ export default {
resolve({ jsnext: true, preferBuiltins: true, browser: true }),
replace({
values: {
'import.meta.env.VITE_IFRAME_URL': JSON.stringify(new URL(process.env.VITE_IFRAME_URL).origin),
'import.meta.env.VITE_API_URL': JSON.stringify(new URL(process.env.VITE_API_URL).origin),
...ENV_TABLE.reduce((acc, env) => {
acc[`import.meta.env.${env.name}`] = JSON.stringify(new URL(env.value).origin);
return acc
}, {}),
'import.meta.env.VITE_ANALYTICS_LIB_PK': JSON.stringify(process.env.VITE_ANALYTICS_LIB_PK),
'import.meta.env.VITE_ANALYTICS_SUBDOMAIN_URL': JSON.stringify(process.env.VITE_ANALYTICS_SUBDOMAIN_URL),
},
preventAssignment: true
}),
Expand Down
13 changes: 8 additions & 5 deletions src/lib/StringPay.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export interface StringPayload {
options?: StringOptions;
apiKey: string;
name: string;
collection?: string;
currency: string;
Expand All @@ -17,16 +15,21 @@ export interface StringPayload {
gasLimit?: string;
}

export type StringSDKEnvironment = "PROD" | "SANDBOX" | "DEV" | "LOCAL";

export interface StringOptions {
env: StringSDKEnvironment;
publicKey: string;
bypassDeviceCheck?: boolean;
}

export declare class StringPay {
container?: Element;
frame?: HTMLIFrameElement;
payload?: StringPayload;
isLoaded: boolean;
payload?: StringPayload;
frame?: HTMLIFrameElement;
container?: Element;
onFrameLoad: () => void;
onFrameClose: () => void;
init(options: StringOptions): void;
loadFrame(payload: StringPayload): void;
}
90 changes: 58 additions & 32 deletions src/lib/StringPay.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { createServices, type Services } from "./services";

export interface StringPayload {
options?: StringOptions
apiKey: string;
name: string;
collection?: string;
currency: string;
Expand All @@ -19,32 +17,71 @@ export interface StringPayload {
gasLimit?: string;
}

export type StringSDKEnvironment = "PROD" | "SANDBOX" | "DEV" | "LOCAL";

export interface StringOptions {
env: StringSDKEnvironment;
publicKey: string;
bypassDeviceCheck?: boolean;
}

const IFRAME_URL = import.meta.env.VITE_IFRAME_URL;
const API_URL = import.meta.env.VITE_API_URL;
type StringEnvDetails = {
IFRAME_URL: string;
API_URL: string;
}

const ENV_TABLE: Record<StringSDKEnvironment, StringEnvDetails> = {
"PROD": {
IFRAME_URL: import.meta.env.PROD_IFRAME_URL,
API_URL: import.meta.env.PROD_API_URL,
},
"SANDBOX": {
IFRAME_URL: import.meta.env.SBOX_IFRAME_URL,
API_URL: import.meta.env.SBOX_API_URL,
},
"DEV": {
IFRAME_URL: import.meta.env.DEV_IFRAME_URL,
API_URL: import.meta.env.DEV_API_URL,
},
"LOCAL": {
IFRAME_URL: import.meta.env.LOCAL_IFRAME_URL,
API_URL: import.meta.env.LOCAL_API_URL,
}
}

const err = (msg: string) => {
console.error("[String Pay] " + msg);
};

export class StringPay {
container?: Element;
frame?: HTMLIFrameElement;
payload?: StringPayload;
isLoaded = false;
services: Services;
private _loadIframeCallback = () => {};

constructor(loadIframeCallback: () => void) {
this._loadIframeCallback = loadIframeCallback;
}
payload?: StringPayload;
frame?: HTMLIFrameElement;
container?: Element;
#services: Services;
private _IFRAME_URL: string;

onFrameLoad = () => {};
onFrameClose = () => {};

init(options: StringOptions) {
const envDetails = ENV_TABLE[options.env];
if (!envDetails) {
return err(`Invalid environment: ${options.env}`);
}

if (!options.publicKey) return err("You need an API key to use the String SDK");
if (options.publicKey.slice(0, 4) !== "str.") return err(`Invalid API Key: ${options.publicKey}`);

this._IFRAME_URL = envDetails.IFRAME_URL;
this.#services = createServices({
baseUrl: envDetails.API_URL,
iframeUrl: envDetails.IFRAME_URL,
apiKey: options.publicKey,
bypassDeviceCheck: options.bypassDeviceCheck ?? false
});
}

async loadFrame(payload: StringPayload) {
// make sure there is a wallet connected
if (!window.ethereum || !window.ethereum.selectedAddress) return err("No wallet connected, please connect wallet");
Expand All @@ -59,10 +96,9 @@ export class StringPay {

// Validate payload
if (!payload) return err("No payload specified");
if (!payload.apiKey) return err("You must have an api key in your payload");
if (payload.apiKey.slice(0, 4) !== "str.") return err(`Invalid API Key: ${payload.apiKey}`);
if (!payload.userAddress) return err("No user address found, please connect wallet");
if (!IFRAME_URL) return err("IFRAME_URL not specified");
if (!this._IFRAME_URL) return err("IFRAME_URL not specified. Did you call init()?");
if (!this.#services) return err("Services not initialized. Did you call init()?");

// Set payload
this.payload = payload;
Expand All @@ -72,34 +108,24 @@ export class StringPay {
iframe.style.width = "100vh";
iframe.style.height = "700px";
iframe.style.overflow = "none";
iframe.src = IFRAME_URL;
iframe.src = this._IFRAME_URL;

container.appendChild(iframe);
this.container = container;
this.frame = iframe;

// set the default gas limit
this.payload.gasLimit = "8000000"; // TODO: Do we want this value to change dynamically?

this._loadIframeCallback();
this.#services.eventsService.unregisterEvents();
this.#services.eventsService.registerEvents();
}
}

function main() {
// This is the starting point of the library
// We create services that contain all the logic for the library
// StringPay is the main class that is exported to the user
// We also create a callback that is called when the iframe is loaded. This is used to register events

const services = createServices({
apiUrl: API_URL,
});

const loadIframeCallback = () => {
services.eventsService.unregisterEvents();
services.eventsService.registerEvents();
};
// Expose the StringPay instance to the window

const stringPay = new StringPay(loadIframeCallback);
const stringPay = new StringPay();
(<any>window).StringPay = stringPay;
}

Expand Down
9 changes: 3 additions & 6 deletions src/lib/services/apiClient.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@ import axios from "redaxios";

// TODO: Fix timeout issue

export function createApiClient({ baseUrl }: ApiClientOptions): ApiClient {
export function createApiClient({ baseUrl, apiKey }: ApiClientOptions): ApiClient {
let _userWalletAddress = "";
let apiKey = "";

const commonHeaders: any = {
"Content-Type": "application/json",
Expand All @@ -18,7 +17,6 @@ export function createApiClient({ baseUrl }: ApiClientOptions): ApiClient {
});

const setWalletAddress = (addr: string) => (_userWalletAddress = addr);
const setApiKey = (key: string) => (apiKey = key);

async function createApiKey() {
const { data } = await httpClient.post<{ apiKey: string }>("/apikeys");
Expand Down Expand Up @@ -230,8 +228,7 @@ export function createApiClient({ baseUrl }: ApiClientOptions): ApiClient {
getUserStatus,
getQuote,
transact,
setWalletAddress,
setApiKey,
setWalletAddress
};
}

Expand All @@ -250,7 +247,6 @@ export interface ApiClient {
getQuote: (payload: QuoteRequestPayload) => Promise<TransactPayload>;
transact: (quote: TransactPayload) => Promise<TransactionResponse>;
setWalletAddress: (walletAddress: string) => void;
setApiKey: (apiKey: string) => void;
}

interface ApiKeyResponse {
Expand Down Expand Up @@ -339,4 +335,5 @@ export interface QuoteRequestPayload {

export interface ApiClientOptions {
baseUrl: string;
apiKey: string;
}
20 changes: 9 additions & 11 deletions src/lib/services/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
import type { ApiClient, User } from './apiClient.service';
import type { LocationService, VisitorData } from './location.service';

export function createAuthService({ apiClient, locationService }: { apiClient: ApiClient, locationService: LocationService }): AuthService {
export function createAuthService({ apiClient, locationService, bypassDeviceCheck }: AuthServiceParams): AuthService {
const previousAttempt = { signature: "", nonce: "" };

let _bypassDeviceCheck = false;

const setBypassDeviceCheck = (value?: boolean) => {
_bypassDeviceCheck = value || false;
};

const login = async (nonce: string, signature: string, visitorData?: VisitorData) => {
const data = await apiClient.loginUser(nonce, signature, visitorData, _bypassDeviceCheck);
const data = await apiClient.loginUser(nonce, signature, visitorData, bypassDeviceCheck);
return data;
};

Expand Down Expand Up @@ -85,15 +79,19 @@ export function createAuthService({ apiClient, locationService }: { apiClient: A
loginOrCreateUser,
fetchLoggedInUser,
retryLogin,
logout,
setBypassDeviceCheck
logout
};
}

export interface AuthServiceParams {
apiClient: ApiClient;
locationService: LocationService;
bypassDeviceCheck: boolean;
}

export interface AuthService {
loginOrCreateUser: (walletAddress: string) => Promise<{ user: User }>;
fetchLoggedInUser: (walletAddress: string) => Promise<User | null>;
retryLogin: () => Promise<{ user: User }>;
logout: () => Promise<any>;
setBypassDeviceCheck: (value?: boolean) => void;
}
Loading