Files
waseel/lib/areeba.ts
Krikorios 62dc5e9d53 Rebrand to Waseel, swap Stripe for Areeba, add phone-ready auth and DB seed
- Rename app: Waseel (name, slug, scheme waseel://, com.waseel.app ids, splash)
- Payments: replace Stripe with Areeba hosted checkout (create/verify API routes,
  lib/areeba.ts, WebBrowser-based payment flow)
- Maps: migrate address autocomplete to Places API (New), drop legacy library
- Web support: map stub for web (native maps are iOS/Android only)
- Auth: keep email + Google OAuth; fix OAuth redirect for Expo Go
- Add scripts/seed-db.mjs (schema + Lebanese driver seed)
- Pin Expo SDK 51 compatible package versions
2026-08-22 15:53:41 +03:00

116 lines
3.6 KiB
TypeScript

// Server-side helpers for the Areeba payment gateway (MPGS-style API).
//
// Areeba issues merchant credentials after onboarding:
// AREEBA_API_BASE_URL e.g. https://<your-gateway-host>.areeba.com
// AREEBA_MERCHANT_ID your merchant id
// AREEBA_API_PASSWORD API password for the merchant
// AREEBA_API_VERSION gateway REST API version (default: 100)
//
// NOTE: verify the exact field names against the integration docs Areeba
// sends you — the gateway is Mastercard Payment Gateway (MPGS) based, and
// the payloads below follow that convention.
export const areebaConfig = () => {
const baseUrl = process.env.AREEBA_API_BASE_URL?.replace(/\/$/, "");
const merchantId = process.env.AREEBA_MERCHANT_ID;
const apiPassword = process.env.AREEBA_API_PASSWORD;
const apiVersion = process.env.AREEBA_API_VERSION || "100";
if (!baseUrl || !merchantId || !apiPassword) {
throw new Error(
"Missing Areeba configuration. Set AREEBA_API_BASE_URL, AREEBA_MERCHANT_ID and AREEBA_API_PASSWORD in .env",
);
}
return { baseUrl, merchantId, apiPassword, apiVersion };
};
const authHeader = (merchantId: string, apiPassword: string) =>
`Basic ${Buffer.from(`merchant.${merchantId}:${apiPassword}`).toString("base64")}`;
// Creates a checkout session and returns the hosted payment page URL.
export const createCheckoutSession = async ({
orderId,
amount,
currency,
description,
returnUrl,
}: {
orderId: string;
amount: number; // major units, e.g. 25.5 USD
currency: string;
description: string;
returnUrl: string;
}) => {
const { baseUrl, merchantId, apiPassword, apiVersion } = areebaConfig();
const res = await fetch(
`${baseUrl}/api/rest/version/${apiVersion}/merchant/${merchantId}/session`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: authHeader(merchantId, apiPassword),
},
body: JSON.stringify({
apiOperation: "INITIATE_CHECKOUT",
order: {
id: orderId,
amount: amount.toFixed(2),
currency,
description,
},
interaction: {
operation: "PURCHASE",
merchant: { name: "Waseel" },
returnUrl,
},
}),
},
);
const data = await res.json();
if (!res.ok || !data?.session?.id) {
console.log("[AREEBA_CREATE_SESSION]: ", data);
throw new Error(
data?.error?.explanation || "Failed to create Areeba checkout session",
);
}
return {
sessionId: data.session.id as string,
// Used to verify the redirect result (compare with resultIndicator).
successIndicator: data.successIndicator as string | undefined,
checkoutUrl: `${baseUrl}/checkout/pay/${data.session.id}?checkoutVersion=1.0.0`,
};
};
// Retrieves an order and reports whether it was paid.
export const retrieveOrder = async (orderId: string) => {
const { baseUrl, merchantId, apiPassword, apiVersion } = areebaConfig();
const res = await fetch(
`${baseUrl}/api/rest/version/${apiVersion}/merchant/${merchantId}/order/${orderId}`,
{ headers: { Authorization: authHeader(merchantId, apiPassword) } },
);
const data = await res.json();
if (!res.ok) {
console.log("[AREEBA_RETRIEVE_ORDER]: ", data);
throw new Error(
data?.error?.explanation || "Failed to retrieve Areeba order",
);
}
return {
status: data.status as string | undefined,
result: data.result as string | undefined,
amount: data.amount as string | undefined,
currency: data.currency as string | undefined,
// PURCHASE auto-captures; CAPTURED means the money was taken.
paid: data.result === "SUCCESS" && data.status === "CAPTURED",
};
};