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
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#### Bundle Vars

#### PRODUCTION
PROD_IFRAME_URL=https://iframe.string-api.xyz
PROD_API_URL=https://api.string-api.xyz
Expand All @@ -17,4 +19,10 @@ LOCAL_API_URL=http://localhost:5555
VITE_ANALYTICS_LIB_PK=
VITE_ANALYTICS_SUBDOMAIN_URL=https://metrics.string.xyz


#### Test Page Vars

VITE_IPFS_GATEWAY=https://nftstorage.link/ipfs/
VITE_IPFS_CID=bafybeieqi56p6vlxofj6wkoort2m5r72ajhtikpzo53wnyze5isvn34fze
VITE_STRING_API_KEY=str...

5 changes: 2 additions & 3 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
{
"tabWidth": 4,
"useTabs": false,
"useTabs": true,
"semi": true,
"singleQuote": false,
"trailingComma": "all",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"bracketSameLine": false,
"arrowParens": "always",
"printWidth": 160
}
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,18 @@ npm i @stringpay/sdk
yarn @stringpay/sdk
```

Load the SDK's script on your app like so:
Load the SDK's script in the root of your app like so:

```HTML
<script src="../node_modules/@stringpay/sdk/dist/stringpay-v0.0.1.min.js"></script>
<script src="../node_modules/@stringpay/sdk/dist/stringpay-v0.2.0.min.js"></script>
```

Initialize the SDK in onMount (client side):
```JS
window.StringPay.init({
env: "LOCAL", // Can be PROD, SANDBOX, DEV, or LOCAL
publicKey: apiKey,
});
```

With this in place, add your `String Checkout` button anywhere you'd like:
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@stringpay/sdk",
"version": "0.1.8",
"version": "0.2.0",
"license": "UNLICENSED",
"main": "./src/lib/index.ts",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>String SDK Test</title>
<script src="../dist/stringpay-v0.1.8.min.js"></script>
<script src="../dist/stringpay-v0.2.0.min.js"></script>
%sveltekit.head%
</head>
<body>
Expand Down
115 changes: 44 additions & 71 deletions src/lib/services/apiClient.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ export function createApiClient({ baseUrl, apiKey }: ApiClientOptions): ApiClien
"Content-Type": "application/json",
};

const authHeaders: any = {
"X-Api-Key": apiKey,
}

const httpClient = axios.create({
baseURL: baseUrl,
headers: commonHeaders,
Expand All @@ -18,31 +22,15 @@ export function createApiClient({ baseUrl, apiKey }: ApiClientOptions): ApiClien

const setWalletAddress = (addr: string) => (_userWalletAddress = addr);

async function createApiKey() {
const { data } = await httpClient.post<{ apiKey: string }>("/apikeys");
return data;
}

async function getApiKeys(limit = 10) {
const { data } = await httpClient.get<ApiKeyResponse[]>("/apikeys", {
params: { limit },
});
return data;
}

async function validateApiKey(keyId: string) {
const { data } = await httpClient.post<{ Status: string }>(`/apikeys/${keyId}/approve`);
return data;
}

async function requestLogin(walletAddress: string) {
setWalletAddress(walletAddress);

try {
const headers = { "X-Api-Key": apiKey };
const { data } = await httpClient.get<{ nonce: string }>(`/login`, {
params: { walletAddress: _userWalletAddress },
headers,
headers: authHeaders,
});

return data;
} catch (e: any) {
const error = _getErrorFromAxiosError(e);
Expand All @@ -51,18 +39,16 @@ export function createApiClient({ baseUrl, apiKey }: ApiClientOptions): ApiClien
}

async function createUser(nonce: string, signature: string, visitor?: VisitorData) {
const headers = { "X-Api-Key": apiKey };
const body = {
nonce,
signature,
fingerprint: visitor,
};

try {
const { data } = await httpClient.post<{
authToken: AuthToken;
user: User;
}>(`/users`, body, { headers });
const { data } = await httpClient.post<AuthResponse>(`/users`, body, {
headers: authHeaders,
});
return data;
} catch (e: any) {
const error = _getErrorFromAxiosError(e);
Expand All @@ -71,20 +57,21 @@ export function createApiClient({ baseUrl, apiKey }: ApiClientOptions): ApiClien
}

async function updateUser(userId: string, update: UserUpdate) {
const request = () =>
httpClient.put<User>(`/users/${userId}`, update, {
headers: { "X-Api-Key": apiKey },
});
try {
const request = () => httpClient.put<User>(`/users/${userId}`, update);
const { data } = await authInterceptor<{ data: User }>(request);

const { data } = await authInterceptor<{ data: User }>(request);
return data;
return data;
} catch (e: any) {
const error = _getErrorFromAxiosError(e);
throw error;
}
}

async function requestEmailVerification(userId: string, email: string) {
try {
const request = () =>
httpClient.get(`/users/${userId}/verify-email`, {
headers: { "X-Api-Key": apiKey },
params: { email },
// timeout: 15 * 60 * 1000 // 15 minutes
});
Expand All @@ -97,18 +84,18 @@ export function createApiClient({ baseUrl, apiKey }: ApiClientOptions): ApiClien
}

async function loginUser(nonce: string, signature: string, visitor?: VisitorData, bypassDeviceCheck = false) {
const headers = { "X-Api-Key": apiKey };
const body = {
nonce,
signature,
fingerprint: visitor,
};

const bypassDevice = bypassDeviceCheck ? "?bypassDevice=true" : "";

try {
const { data } = await httpClient.post<{
authToken: AuthToken;
user: User;
}>(`/login/sign${bypassDeviceCheck ? "?bypassDevice=true" : ""}`, body, { headers });
const { data } = await httpClient.post<AuthResponse>(`/login/sign${bypassDevice}`, body, {
headers: authHeaders,
});
return data;
} catch (e: any) {
const error = _getErrorFromAxiosError(e);
Expand All @@ -117,13 +104,12 @@ export function createApiClient({ baseUrl, apiKey }: ApiClientOptions): ApiClien
}

async function refreshToken(walletAddress: string) {
const headers = { "X-Api-Key": apiKey };
try {
const { data } = await httpClient.post<{
authToken: AuthToken;
user: User;
}>(`/login/refresh`, { walletAddress }, { headers });
console.log(" - Token was refreshed");
const { data } = await httpClient.post<AuthResponse>(`/login/refresh`, { walletAddress }, {
headers: authHeaders,
});
console.debug(" - Token was refreshed");

return data;
} catch (e: any) {
const error = _getErrorFromAxiosError(e);
Expand All @@ -132,25 +118,22 @@ export function createApiClient({ baseUrl, apiKey }: ApiClientOptions): ApiClien
}

async function logoutUser() {
const headers = { "X-Api-Key": apiKey };
try {
const { status } = await httpClient.post(`/login/logout`, {}, { headers });
if (status === 204) return;
else throw new Error("logout failed");
const { status } = await httpClient.post(`/login/logout`);
if (status !== 204) throw new Error("logout failed");
} catch (e: any) {
const error = _getErrorFromAxiosError(e);
throw error;
}
}

async function getUserStatus(userId: string) {
if (!userId) throw new Error("userId is required");
const headers = { "X-Api-Key": apiKey };
try {
const request = () => httpClient.get(`/users/${userId}/status`, { headers });
const request = () => httpClient.get(`/users/${userId}/status`);
const { data } = await authInterceptor<{
data: { status: string };
}>(request);

return data;
} catch (e: any) {
const error = _getErrorFromAxiosError(e);
Expand All @@ -159,10 +142,10 @@ export function createApiClient({ baseUrl, apiKey }: ApiClientOptions): ApiClien
}

async function getQuote(payload: QuoteRequestPayload) {
const headers = { "X-Api-Key": apiKey };
try {
const request = () => httpClient.post(`/quotes`, payload, { headers });
const request = () => httpClient.post(`/quotes`, payload);
const { data } = await authInterceptor<{ data: TransactPayload }>(request);

return data;
} catch (e: any) {
const error = _getErrorFromAxiosError(e);
Expand All @@ -171,12 +154,12 @@ export function createApiClient({ baseUrl, apiKey }: ApiClientOptions): ApiClien
}

async function transact(transactPayload: TransactPayload) {
const headers = { "X-Api-Key": apiKey };
try {
const request = () => httpClient.post(`/transactions`, transactPayload, { headers });
const request = () => httpClient.post(`/transactions`, transactPayload);
const { data } = await authInterceptor<{
data: TransactionResponse;
}>(request);

return data;
} catch (e: any) {
const error = _getErrorFromAxiosError(e);
Expand Down Expand Up @@ -215,9 +198,6 @@ export function createApiClient({ baseUrl, apiKey }: ApiClientOptions): ApiClien
}

return {
createApiKey,
getApiKeys,
validateApiKey,
requestLogin,
createUser,
updateUser,
Expand All @@ -233,31 +213,19 @@ export function createApiClient({ baseUrl, apiKey }: ApiClientOptions): ApiClien
}

export interface ApiClient {
createApiKey: () => Promise<{ apiKey: string }>;
getApiKeys: () => Promise<ApiKeyResponse[]>;
validateApiKey: (keyId: string) => Promise<{ Status: string }>;
requestLogin: (walletAddress: string) => Promise<{ nonce: string }>;
createUser: (nonce: string, signature: string, visitor?: VisitorData) => Promise<{ authToken: AuthToken; user: User }>;
createUser: (nonce: string, signature: string, visitor?: VisitorData) => Promise<AuthResponse>;
updateUser: (userId: string, userUpdate: UserUpdate) => Promise<User>;
requestEmailVerification: (userId: string, email: string) => Promise<void>;
loginUser: (nonce: string, signature: string, visitor?: VisitorData, bypassDeviceCheck?: boolean) => Promise<{ authToken: AuthToken; user: User }>;
refreshToken: (walletAddress: string) => Promise<{ authToken: AuthToken; user: User }>;
loginUser: (nonce: string, signature: string, visitor?: VisitorData, bypassDeviceCheck?: boolean) => Promise<AuthResponse>;
refreshToken: (walletAddress: string) => Promise<AuthResponse>;
logoutUser: () => Promise<void>;
getUserStatus: (userId: string) => Promise<{ status: string }>;
getQuote: (payload: QuoteRequestPayload) => Promise<TransactPayload>;
transact: (quote: TransactPayload) => Promise<TransactionResponse>;
setWalletAddress: (walletAddress: string) => void;
}

interface ApiKeyResponse {
id: string;
status: string;
authType: string;
data: string;
createdAt: string;
updatedAt: string;
}

interface RefreshToken {
token: string;
expAt: Date;
Expand All @@ -270,6 +238,11 @@ interface AuthToken {
expAt: string;
}

export interface AuthResponse {
authToken: AuthToken;
user: User;
}

export interface UserUpdate {
walletAddress?: string;
firstName?: string;
Expand Down
20 changes: 12 additions & 8 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,25 @@
import { writable } from "svelte/store";
import { ethers } from "ethers";

const apiKey = import.meta.env.VITE_STRING_API_KEY;

const signerAddress = writable("");

const STR_API_KEY = import.meta.env.VITE_STRING_API_KEY;

const IPFS_GATEWAY = import.meta.env.VITE_IPFS_GATEWAY
const IPFS_CID = import.meta.env.VITE_IPFS_CID

const STR_NFT_SRC = `${IPFS_GATEWAY}${IPFS_CID}/Demo_Character_1.png`

$: payload = {
name: "String Demo NFT",
name: "String Test NFT [AVAX]",
collection: "String Demo",
imageSrc:
"https://gateway.pinata.cloud/ipfs/bafybeibtmy26mac47n5pp6srds76h74riqs76erw24p5yvdhmwu7pxlcx4/STR_Logo_1.png",
imageAlt: "NFT",
imageSrc: STR_NFT_SRC,
imageAlt: "String NFT",
currency: "AVAX",
price: 0.08,
chainID: 43113,
userAddress: $signerAddress,
contractAddress: "0x41e11fF9F71f51800F67cb913eA6Bc59d3F126Aa",
contractAddress: "0xea1ffe2cf6630a20e1ba397e95358daf362c8781",
contractFunction: "mintTo(address)",
contractReturn: "uint256",
contractParameters: [$signerAddress],
Expand All @@ -34,7 +38,7 @@

window.StringPay.init({
env: "LOCAL",
publicKey: apiKey
publicKey: STR_API_KEY,
});


Expand Down