Skip to content

Commit 130a541

Browse files
chore: added POC pos project (#313)
1 parent e3118c9 commit 130a541

File tree

109 files changed

+22704
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+22704
-0
lines changed

dapps/poc-pos-app/.env.example

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
EXPO_PUBLIC_PROJECT_ID=""
2+
EXPO_PUBLIC_SENTRY_DSN=""
3+
SENTRY_AUTH_TOKEN=""
4+
EXPO_PUBLIC_API_URL=""
5+
EXPO_PUBLIC_GATEWAY_URL=""
6+
EXPO_PUBLIC_MERCHANTS_URL=""

dapps/poc-pos-app/.gitignore

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
2+
3+
# dependencies
4+
node_modules/
5+
6+
# Expo
7+
.expo/
8+
dist/
9+
web-build/
10+
expo-env.d.ts
11+
12+
# Native
13+
.kotlin/
14+
*.orig.*
15+
*.jks
16+
*.p8
17+
*.p12
18+
*.key
19+
*.mobileprovision
20+
21+
# Metro
22+
.metro-health-check*
23+
24+
# debug
25+
npm-debug.*
26+
yarn-debug.*
27+
yarn-error.*
28+
29+
# macOS
30+
.DS_Store
31+
*.pem
32+
33+
# local env files
34+
.env*.local
35+
36+
# typescript
37+
*.tsbuildinfo
38+
39+
# Skia Web artifacts
40+
public/
41+
web/
42+
43+
app-example
44+
45+
.env.local
46+
47+
ios/*
48+
android/*

dapps/poc-pos-app/README.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# Welcome to POC POS Sample app
2+
3+
> ⚠️ **WARNING: This is legacy code and is no longer actively maintained.** Use at your own risk.
4+
5+
## Prerequisites
6+
7+
Before you begin, make sure you have set up your React Native development environment. This includes installing Node, Android Studio (for Android), and Xcode (for iOS on macOS).
8+
9+
Follow the official React Native documentation to set up your environment:
10+
- [React Native Environment Setup](https://reactnative.dev/docs/set-up-your-environment?platform=android)
11+
12+
## Get started
13+
14+
1. Install dependencies
15+
16+
```bash
17+
npm install
18+
```
19+
20+
2. Set up environment variables
21+
22+
Create a `.env` file using the example template:
23+
24+
```bash
25+
cp .env.example .env
26+
```
27+
28+
Update the `.env` file with your configuration values.
29+
30+
3. Create native folders
31+
32+
```bash
33+
npm run prebuild
34+
```
35+
36+
This will automatically set up the required files for development.
37+
38+
4. Start the app
39+
40+
```bash
41+
npm run android
42+
```
43+
44+
```bash
45+
npm run ios
46+
```
47+
48+
```bash
49+
npm run web
50+
```
51+
52+
## Production Releases
53+
54+
For production Android releases, you'll need the actual `secrets.properties` file and `wc_rn_upload.keystore`. Get these from the mobile team or 1Password.
55+
56+
**Required file locations:**
57+
- Place `secrets.properties` in the `android/` directory
58+
- Place `wc_rn_upload.keystore` in the `android/app/` directory
59+
60+
```
61+
android/
62+
├── secrets.properties ← Place here
63+
└── app/
64+
└── wc_rn_upload.keystore ← Place here
65+
```
66+
67+
**Build the release APK:**
68+
69+
```bash
70+
npm run android:build
71+
```
72+
73+
The release APK will be generated at `android/app/build/outputs/apk/release/app-release.apk`
74+
75+
**Install on a device via USB:**
76+
77+
> **Note**: Make sure USB debugging is enabled on your Android device. Go to Settings → About phone → Tap "Build number" 7 times → Developer options → Enable "USB debugging".
78+
79+
1. Connect your device via USB and get its device ID:
80+
81+
```bash
82+
adb devices
83+
```
84+
85+
Example output: `V510BAC07114B000171`
86+
87+
2. Build the release APK:
88+
89+
```bash
90+
npm run android:build
91+
```
92+
93+
3. Install the APK on your device (replace with your device ID):
94+
95+
```bash
96+
adb -s V510BAC07114B000171 install android/app/build/outputs/apk/release/app-release.apk
97+
```
98+
99+
> **⚠️ Security Note**: Never commit `secrets.properties` or keystore files to version control.
100+
101+
## Creating Custom Variants
102+
103+
To create a branded variant for a specific client:
104+
105+
1. **Create a new branch**
106+
```bash
107+
git checkout -b variant/<client-name>
108+
```
109+
110+
2. **Add the variant logo**
111+
- Add the client's logo to `assets/images/variants/<client-name>_brand.png`
112+
- **Requirements**: PNG format
113+
114+
3. **Add the printer logo** in `constants/printer-logos.ts`
115+
- Convert the client's logo to base64 using https://base64.guru/converter/encode/image/png
116+
- Add the base64 string as a new constant:
117+
```typescript
118+
export const <CLIENT_NAME>_LOGO_BASE64 =
119+
"data:image/png;base64,<base64-string>";
120+
```
121+
122+
4. **Define the variant** in `constants/variants.ts`
123+
- Import the printer logo at the top of the file:
124+
```typescript
125+
import {
126+
// ... existing imports ...
127+
<CLIENT_NAME>_LOGO_BASE64,
128+
} from "./printer-logos";
129+
```
130+
- Add the variant name to the `VariantName` type:
131+
```typescript
132+
export type VariantName =
133+
| "default"
134+
| "solflare"
135+
| "binance"
136+
| "phantom"
137+
| "solana"
138+
| "<client-name>";
139+
```
140+
- Add the variant configuration to the `Variants` object:
141+
```typescript
142+
<client-name>: {
143+
name: "<Client Name>",
144+
brandLogo: require("@/assets/images/variants/<client-name>_brand.png"),
145+
printerLogo: <CLIENT_NAME>_LOGO_BASE64,
146+
defaultTheme: "light", // or "dark"
147+
colors: {
148+
light: {
149+
"icon-accent-primary": "#HEXCOLOR",
150+
"bg-accent-primary": "#HEXCOLOR",
151+
"bg-payment-success": "#HEXCOLOR",
152+
"text-payment-success": "#HEXCOLOR",
153+
"border-payment-success": "#HEXCOLOR",
154+
},
155+
dark: {
156+
// Same color keys as light theme
157+
},
158+
},
159+
},
160+
```
161+
162+
163+
5. **Update Android version code** in `app.json`
164+
- Increment `expo.android.versionCode`
165+
166+
6. **Commit, push, and create a release tag (Devin only)**
167+
```bash
168+
git add .
169+
git commit -m "feat: add <client-name> variant"
170+
git push origin variant/<client-name>
171+
git tag variant-<client-name>
172+
git push origin variant-<client-name>
173+
```
174+
The tag will trigger the release workflow automatically.
175+
176+
7. **Verify the release**
177+
- Check the build status and Firebase link in the `#system-releases-react-native` Slack channel
178+
179+
**Manual release**: If you need to trigger the release manually instead of using a tag, go to GitHub Actions and run the `release-pos-poc` workflow.

dapps/poc-pos-app/api/client.ts

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import { useLogsStore } from "@/store/useLogsStore";
2+
import { ApiError } from "@/utils/types";
3+
4+
const API_BASE_URL = process.env.EXPO_PUBLIC_API_URL;
5+
if (!API_BASE_URL) {
6+
throw new Error("EXPO_PUBLIC_API_URL environment variable is not configured");
7+
}
8+
9+
const MERCHANT_API_BASE_URL = process.env.EXPO_PUBLIC_MERCHANTS_URL;
10+
if (!MERCHANT_API_BASE_URL) {
11+
throw new Error(
12+
"EXPO_PUBLIC_MERCHANTS_URL environment variable is not configured",
13+
);
14+
}
15+
16+
interface RequestOptions extends Omit<RequestInit, "body"> {
17+
body?: unknown;
18+
}
19+
20+
class ApiClient {
21+
private baseUrl: string;
22+
23+
constructor(baseUrl: string) {
24+
this.baseUrl = baseUrl;
25+
}
26+
27+
private async request<T>(
28+
endpoint: string,
29+
options: RequestOptions = {},
30+
): Promise<T> {
31+
const { body, headers, ...fetchOptions } = options;
32+
33+
// Normalize URL construction: remove trailing slash from baseUrl and ensure endpoint starts with /
34+
const normalizedBaseUrl = this.baseUrl.replace(/\/+$/, "");
35+
const normalizedEndpoint = endpoint.startsWith("/")
36+
? endpoint
37+
: `/${endpoint}`;
38+
const url = `${normalizedBaseUrl}${normalizedEndpoint}`;
39+
40+
const requestHeaders: HeadersInit = {
41+
"Content-Type": "application/json",
42+
...headers,
43+
};
44+
45+
const config: RequestInit = {
46+
...fetchOptions,
47+
headers: requestHeaders,
48+
};
49+
50+
if (body) {
51+
config.body = JSON.stringify(body);
52+
}
53+
54+
try {
55+
const response = await fetch(url, config);
56+
57+
if (!response.ok) {
58+
const errorData = await this.parseErrorResponse(response);
59+
const error: ApiError = {
60+
message:
61+
errorData.message || `HTTP error! status: ${response.status}`,
62+
code: errorData.code,
63+
status: response.status,
64+
};
65+
throw error;
66+
}
67+
68+
const data = await response.json();
69+
useLogsStore
70+
.getState()
71+
.addLog("info", "API request successful", "api", "request", {
72+
endpoint,
73+
body,
74+
response: data,
75+
});
76+
return data as T;
77+
} catch (error) {
78+
if (error && typeof error === "object" && "status" in error) {
79+
const apiError = error as ApiError;
80+
useLogsStore
81+
.getState()
82+
.addLog(
83+
"error",
84+
apiError.message || "API request failed",
85+
"api",
86+
"request",
87+
{ endpoint, body, response: error },
88+
);
89+
throw error;
90+
}
91+
const errorMessage =
92+
error instanceof Error ? error.message : "An unexpected error occurred";
93+
useLogsStore
94+
.getState()
95+
.addLog("error", errorMessage, "api", "request", { endpoint });
96+
const apiError: ApiError = {
97+
message: errorMessage,
98+
};
99+
throw apiError;
100+
}
101+
}
102+
103+
private async parseErrorResponse(
104+
response: Response,
105+
): Promise<{ message?: string; code?: string }> {
106+
try {
107+
return await response.json();
108+
} catch {
109+
return { message: response.statusText };
110+
}
111+
}
112+
113+
async get<T>(endpoint: string, options?: RequestOptions): Promise<T> {
114+
return this.request<T>(endpoint, { ...options, method: "GET" });
115+
}
116+
117+
async post<T>(
118+
endpoint: string,
119+
body?: unknown,
120+
options?: RequestOptions,
121+
): Promise<T> {
122+
return this.request<T>(endpoint, { ...options, method: "POST", body });
123+
}
124+
125+
async put<T>(
126+
endpoint: string,
127+
body?: unknown,
128+
options?: RequestOptions,
129+
): Promise<T> {
130+
return this.request<T>(endpoint, { ...options, method: "PUT", body });
131+
}
132+
133+
async delete<T>(endpoint: string, options?: RequestOptions): Promise<T> {
134+
return this.request<T>(endpoint, { ...options, method: "DELETE" });
135+
}
136+
}
137+
138+
export const apiClient = new ApiClient(API_BASE_URL);
139+
140+
export const merchantClient = new ApiClient(MERCHANT_API_BASE_URL);

0 commit comments

Comments
 (0)