a0-purchases
Version:
Lightweight subscription management for AI apps with auto-detecting providers
178 lines • 8.17 kB
JavaScript
import { PURCHASES_ERROR_CODE } from '../types';
import { API_BASE_URL, APP_ID, API_KEY, APP_LIVE } from '../constants';
class BackendClient {
constructor() {
this.config = { debug: false };
}
configure(config) {
this.config = Object.assign(Object.assign({}, this.config), config);
}
logEvent(event, data) {
if (this.config.debug) {
console.log(`[Backend] ${event}`, data ? JSON.stringify(data, null, 2) : '');
}
}
createError(message, code = PURCHASES_ERROR_CODE.UNKNOWN_ERROR, originalError) {
this.logEvent('Error occurred', { message, code, originalError });
const error = new Error(message);
error.code = code;
error.message = message;
error.userCancelled = false;
if (originalError) {
error.underlyingError = originalError;
if (originalError.message && message !== originalError.message) {
error.message = `${message} (Details: ${originalError.message})`;
}
}
return error;
}
async fetchApi(endpoint, options = {}) {
var _a, _b, _c, _d;
const url = `${API_BASE_URL}${endpoint}`;
this.logEvent(`Requesting ${options.method || 'GET'} ${url}`, options.body);
if (!APP_ID || !API_KEY) {
throw this.createError('Missing required configuration: APP_ID or API_KEY', PURCHASES_ERROR_CODE.INVALID_APP_USER_ID_ERROR);
}
try {
const response = await fetch(url, Object.assign(Object.assign({}, options), { headers: Object.assign({ 'Content-Type': 'application/json', 'x-a0-project-id': APP_ID, 'a0-project-key': API_KEY, 'environment': `${APP_LIVE ? 'production' : 'development'}` }, (options.headers || {})) }));
const responseData = await response.json();
if (!response.ok) {
this.logEvent(`Request failed: ${response.status} ${url}`, responseData);
if (((_a = responseData === null || responseData === void 0 ? void 0 : responseData.error) === null || _a === void 0 ? void 0 : _a.code) && ((_b = responseData === null || responseData === void 0 ? void 0 : responseData.error) === null || _b === void 0 ? void 0 : _b.message)) {
throw this.createError(responseData.error.message, responseData.error.code);
}
else if (responseData === null || responseData === void 0 ? void 0 : responseData.message) {
throw this.createError(responseData.message, PURCHASES_ERROR_CODE.UNKNOWN_ERROR, responseData);
}
else {
throw this.createError(`HTTP error ${response.status}`, PURCHASES_ERROR_CODE.UNKNOWN_ERROR, responseData);
}
}
this.logEvent(`Request successful: ${url}`, responseData);
if (responseData.success === false) {
throw this.createError(((_c = responseData.error) === null || _c === void 0 ? void 0 : _c.message) || 'Backend indicated failure', ((_d = responseData.error) === null || _d === void 0 ? void 0 : _d.code) || PURCHASES_ERROR_CODE.UNKNOWN_ERROR, responseData.error);
}
return responseData.data || responseData;
}
catch (error) {
if (error.code && error.message) {
throw error; // Re-throw if it's already a LibraryError
}
this.logEvent(`Network or parsing error: ${url}`, error);
throw this.createError(error.message || 'Network request failed', PURCHASES_ERROR_CODE.UNKNOWN_ERROR, error);
}
}
async fetchOfferings(provider) {
this.logEvent('Fetching offerings from backend', { provider });
const response = await this.fetchApi(`/${APP_ID}/offerings?provider=${provider || 'stripe'}`);
if (response && response.offerings) {
return response.offerings;
}
else {
console.warn('[Backend] No offerings found in response', response);
return { all: {}, current: null };
}
}
async getCustomerInfo(appUserId) {
if (!appUserId) {
throw this.createError('App User ID is required', PURCHASES_ERROR_CODE.INVALID_APP_USER_ID_ERROR);
}
return this.fetchApi(`/customer/${encodeURIComponent(appUserId)}`);
}
async login(appUserId) {
if (!appUserId) {
throw this.createError('App User ID is required for login', PURCHASES_ERROR_CODE.INVALID_APP_USER_ID_ERROR);
}
return this.fetchApi('/login', {
method: 'POST',
body: JSON.stringify({ app_user_id: appUserId, project_id: APP_ID }),
});
}
async postPurchase(appUserId, productId, purchaseToken, transactionId, receipt, platform) {
if (!appUserId) {
throw this.createError('App User ID is required', PURCHASES_ERROR_CODE.INVALID_APP_USER_ID_ERROR);
}
const body = {
app_user_id: appUserId,
product_id: productId,
transaction_receipt: receipt,
};
if (platform === 'ios' && transactionId) {
body.apple_transaction_id = transactionId;
}
else if (platform === 'android' && purchaseToken) {
// Add Android-specific fields if needed
}
this.logEvent('Posting purchase data', body);
return this.fetchApi('/purchase', {
method: 'POST',
body: JSON.stringify(body),
});
}
async fetchPaymentSheetParams(appUserId, pkg) {
return this.fetchApi(`/${APP_ID}/payment-sheet`, {
method: 'POST',
body: JSON.stringify({
app_user_id: appUserId,
pkg: {
product_id: pkg.product.identifier,
package_id: pkg.identifier,
offering_id: pkg.presentedOfferingContext.offeringIdentifier,
},
}),
});
}
async createCheckoutSession(appUserId, pkg) {
return this.fetchApi(`/${APP_ID}/checkout-session`, {
method: 'POST',
body: JSON.stringify({
app_user_id: appUserId,
pkg: {
product_id: pkg.product.identifier,
package_id: pkg.identifier,
offering_id: pkg.presentedOfferingContext.offeringIdentifier,
},
}),
});
}
async createManageSubscriptionSession(appUserId) {
if (!appUserId) {
throw this.createError('App User ID is required', PURCHASES_ERROR_CODE.INVALID_APP_USER_ID_ERROR);
}
return this.fetchApi(`/${APP_ID}/manage-subscription`, {
method: 'POST',
body: JSON.stringify({ app_user_id: appUserId }),
});
}
async getStripeInformation() {
return this.fetchApi(`/${APP_ID}/stripe-info`);
}
async restorePurchases(appUserId, appleTransactionId) {
if (!appUserId) {
throw this.createError('App User ID is required for restore', PURCHASES_ERROR_CODE.INVALID_APP_USER_ID_ERROR);
}
if (!appleTransactionId) {
throw this.createError('Apple transaction ID is required for restore', PURCHASES_ERROR_CODE.PURCHASE_INVALID_ERROR);
}
this.logEvent('Restoring purchases', { appUserId, appleTransactionId });
return this.fetchApi('/restore', {
method: 'POST',
body: JSON.stringify({
app_user_id: appUserId,
apple_transaction_id: appleTransactionId
}),
});
}
async linkAlias(oldId, newId) {
if (!oldId || !newId) {
throw this.createError('Both oldId and newId are required', PURCHASES_ERROR_CODE.INVALID_APP_USER_ID_ERROR);
}
await this.fetchApi(`/app/link-alias`, {
method: 'POST',
body: JSON.stringify({ old_id: oldId, new_id: newId, project_id: APP_ID }),
});
}
}
// Export singleton instance
export const backendClient = new BackendClient();
//# sourceMappingURL=backendClient.js.map