Add payment/notification prefs, assisted pharmacist codes, Rx status journey, and mock payment intents so demo flows match the storefront brief. Co-authored-by: Cursor <cursoragent@cursor.com>
74 lines
2.0 KiB
TypeScript
74 lines
2.0 KiB
TypeScript
/** API client — mock adapters when EXPO_PUBLIC_API_URL is unset. */
|
|
|
|
export const BASE_URLS = {
|
|
core: process.env.EXPO_PUBLIC_API_URL ?? "https://api.gishen.example/v1",
|
|
} as const;
|
|
|
|
export const USE_MOCKS = !process.env.EXPO_PUBLIC_API_URL;
|
|
|
|
export type ApiError = { message: string; code?: string };
|
|
|
|
export type PaymentIntent = {
|
|
id: string;
|
|
provider: string;
|
|
status: "requires_action" | "succeeded" | "pending";
|
|
amountEtb: number;
|
|
checkoutUrl?: string;
|
|
};
|
|
|
|
export async function initiatePayment(input: {
|
|
provider: string;
|
|
amountEtb: number;
|
|
orderId: string;
|
|
}): Promise<PaymentIntent> {
|
|
if (USE_MOCKS) {
|
|
await delay(450);
|
|
return {
|
|
id: `pay_${Date.now()}`,
|
|
provider: input.provider,
|
|
status: input.provider === "bank" || input.provider === "cod" || input.provider === "pay_at_branch"
|
|
? "pending"
|
|
: "succeeded",
|
|
amountEtb: input.amountEtb,
|
|
checkoutUrl:
|
|
input.provider === "chapa" || input.provider === "arifpay"
|
|
? `https://checkout.mock.gishen/${input.provider}`
|
|
: undefined,
|
|
};
|
|
}
|
|
return callEndpoint<PaymentIntent>("/payments/intent", {
|
|
method: "POST",
|
|
body: JSON.stringify(input),
|
|
});
|
|
}
|
|
|
|
export async function callEndpoint<T>(
|
|
path: string,
|
|
init?: RequestInit & { locale?: string }
|
|
): Promise<T> {
|
|
const headers: Record<string, string> = {
|
|
"Content-Type": "application/json",
|
|
Accept: "application/json",
|
|
...(init?.locale ? { "Accept-Language": init.locale } : {}),
|
|
...(init?.headers as Record<string, string>),
|
|
};
|
|
|
|
if (USE_MOCKS) {
|
|
throw new Error(`No mock for ${path} — pass EXPO_PUBLIC_API_URL or add an adapter`);
|
|
}
|
|
|
|
const res = await fetch(`${BASE_URLS.core}${path}`, {
|
|
...init,
|
|
headers,
|
|
});
|
|
if (!res.ok) {
|
|
const body = (await res.json().catch(() => ({}))) as ApiError;
|
|
throw new Error(body.message || res.statusText);
|
|
}
|
|
return res.json() as Promise<T>;
|
|
}
|
|
|
|
function delay(ms: number) {
|
|
return new Promise((r) => setTimeout(r, ms));
|
|
}
|