switched to paddle inline checkout, removed paypal and most of stripe. added product sync between app and paddle.
This commit is contained in:
@@ -119,6 +119,20 @@ export type CreditBalance = {
|
||||
free_event_granted_at?: string | null;
|
||||
};
|
||||
|
||||
export type PaddleTransactionSummary = {
|
||||
id: string | null;
|
||||
status: string | null;
|
||||
amount: number | null;
|
||||
currency: string | null;
|
||||
origin: string | null;
|
||||
checkout_id: string | null;
|
||||
created_at: string | null;
|
||||
updated_at: string | null;
|
||||
receipt_url?: string | null;
|
||||
grand_total?: number | null;
|
||||
tax?: number | null;
|
||||
};
|
||||
|
||||
export type CreditLedgerEntry = {
|
||||
id: number;
|
||||
delta: number;
|
||||
@@ -444,6 +458,25 @@ function normalizeTenantPackage(pkg: JsonValue): TenantPackageSummary {
|
||||
};
|
||||
}
|
||||
|
||||
function normalizePaddleTransaction(entry: JsonValue): PaddleTransactionSummary {
|
||||
const amountValue = entry.amount ?? entry.grand_total ?? (entry.totals && entry.totals.grand_total);
|
||||
const taxValue = entry.tax ?? (entry.totals && entry.totals.tax_total);
|
||||
|
||||
return {
|
||||
id: typeof entry.id === 'string' ? entry.id : entry.id ? String(entry.id) : null,
|
||||
status: entry.status ?? null,
|
||||
amount: amountValue !== undefined && amountValue !== null ? Number(amountValue) : null,
|
||||
currency: entry.currency ?? entry.currency_code ?? 'EUR',
|
||||
origin: entry.origin ?? null,
|
||||
checkout_id: entry.checkout_id ?? (entry.details?.checkout_id ?? null),
|
||||
created_at: entry.created_at ?? null,
|
||||
updated_at: entry.updated_at ?? null,
|
||||
receipt_url: entry.receipt_url ?? entry.invoice_url ?? null,
|
||||
grand_total: entry.grand_total !== undefined && entry.grand_total !== null ? Number(entry.grand_total) : null,
|
||||
tax: taxValue !== undefined && taxValue !== null ? Number(taxValue) : null,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeTask(task: JsonValue): TenantTask {
|
||||
const titleTranslations = normalizeTranslationMap(task.title_translations ?? task.title ?? {});
|
||||
const descriptionTranslations = normalizeTranslationMap(task.description_translations ?? task.description ?? {});
|
||||
@@ -813,6 +846,35 @@ export async function getTenantPackagesOverview(): Promise<{
|
||||
return { packages, activePackage };
|
||||
}
|
||||
|
||||
export async function getTenantPaddleTransactions(cursor?: string): Promise<{
|
||||
data: PaddleTransactionSummary[];
|
||||
nextCursor: string | null;
|
||||
hasMore: boolean;
|
||||
}> {
|
||||
const query = cursor ? `?cursor=${encodeURIComponent(cursor)}` : '';
|
||||
const response = await authorizedFetch(`/api/v1/tenant/billing/transactions${query}`);
|
||||
|
||||
if (response.status === 404) {
|
||||
return { data: [], nextCursor: null, hasMore: false };
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const payload = await safeJson(response);
|
||||
console.error('[API] Failed to load Paddle transactions', response.status, payload);
|
||||
throw new Error('Failed to load Paddle transactions');
|
||||
}
|
||||
|
||||
const payload = await safeJson(response) ?? {};
|
||||
const entries = Array.isArray(payload.data) ? payload.data : [];
|
||||
const meta = payload.meta ?? {};
|
||||
|
||||
return {
|
||||
data: entries.map(normalizePaddleTransaction),
|
||||
nextCursor: typeof meta.next === 'string' ? meta.next : null,
|
||||
hasMore: Boolean(meta.has_more),
|
||||
};
|
||||
}
|
||||
|
||||
export async function getCreditBalance(): Promise<CreditBalance> {
|
||||
const response = await authorizedFetch('/api/v1/tenant/credits/balance');
|
||||
if (response.status === 404) {
|
||||
@@ -868,17 +930,17 @@ export async function createTenantPackagePaymentIntent(packageId: number): Promi
|
||||
export async function completeTenantPackagePurchase(params: {
|
||||
packageId: number;
|
||||
paymentMethodId?: string;
|
||||
paypalOrderId?: string;
|
||||
paddleTransactionId?: string;
|
||||
}): Promise<void> {
|
||||
const { packageId, paymentMethodId, paypalOrderId } = params;
|
||||
const { packageId, paymentMethodId, paddleTransactionId } = params;
|
||||
const payload: Record<string, unknown> = { package_id: packageId };
|
||||
|
||||
if (paymentMethodId) {
|
||||
payload.payment_method_id = paymentMethodId;
|
||||
}
|
||||
|
||||
if (paypalOrderId) {
|
||||
payload.paypal_order_id = paypalOrderId;
|
||||
if (paddleTransactionId) {
|
||||
payload.paddle_transaction_id = paddleTransactionId;
|
||||
}
|
||||
|
||||
const response = await authorizedFetch('/api/v1/tenant/packages/complete', {
|
||||
@@ -904,8 +966,8 @@ export async function assignFreeTenantPackage(packageId: number): Promise<void>
|
||||
await jsonOrThrow(response, 'Failed to assign free package');
|
||||
}
|
||||
|
||||
export async function createTenantPayPalOrder(packageId: number): Promise<string> {
|
||||
const response = await authorizedFetch('/api/v1/tenant/packages/paypal-create', {
|
||||
export async function createTenantPaddleCheckout(packageId: number): Promise<{ checkout_url: string }> {
|
||||
const response = await authorizedFetch('/api/v1/tenant/packages/paddle-checkout', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -913,24 +975,12 @@ export async function createTenantPayPalOrder(packageId: number): Promise<string
|
||||
body: JSON.stringify({ package_id: packageId }),
|
||||
});
|
||||
|
||||
const data = await jsonOrThrow<{ orderID: string }>(response, 'Failed to create PayPal order');
|
||||
if (!data.orderID) {
|
||||
throw new Error('Missing PayPal order ID');
|
||||
const data = await jsonOrThrow<{ checkout_url: string }>(response, 'Failed to create Paddle checkout');
|
||||
if (!data.checkout_url) {
|
||||
throw new Error('Missing Paddle checkout URL');
|
||||
}
|
||||
|
||||
return data.orderID;
|
||||
}
|
||||
|
||||
export async function captureTenantPayPalOrder(orderId: string): Promise<void> {
|
||||
const response = await authorizedFetch('/api/v1/tenant/packages/paypal-capture', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ order_id: orderId }),
|
||||
});
|
||||
|
||||
await jsonOrThrow(response, 'Failed to capture PayPal order');
|
||||
return { checkout_url: data.checkout_url };
|
||||
}
|
||||
|
||||
export async function recordCreditPurchase(payload: {
|
||||
|
||||
Reference in New Issue
Block a user