Add PayPal checkout provider

This commit is contained in:
Codex Agent
2026-02-04 12:18:14 +01:00
parent 56a39d0535
commit fc5dfb272c
33 changed files with 1586 additions and 571 deletions

View File

@@ -2760,11 +2760,11 @@ export async function getTenantLemonSqueezyTransactions(cursor?: string): Promis
};
}
export async function createTenantLemonSqueezyCheckout(
export async function createTenantPayPalCheckout(
packageId: number,
urls?: { success_url?: string; return_url?: string }
): Promise<{ checkout_url: string; id: string; expires_at?: string; checkout_session_id?: string }> {
const response = await authorizedFetch('/api/v1/tenant/packages/lemonsqueezy-checkout', {
): Promise<{ approve_url: string | null; order_id: string; status?: string; checkout_session_id?: string }> {
const response = await authorizedFetch('/api/v1/tenant/packages/paypal-checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -2773,7 +2773,7 @@ export async function createTenantLemonSqueezyCheckout(
return_url: urls?.return_url,
}),
});
return await jsonOrThrow<{ checkout_url: string; id: string; expires_at?: string; checkout_session_id?: string }>(
return await jsonOrThrow<{ approve_url: string | null; order_id: string; status?: string; checkout_session_id?: string }>(
response,
'Failed to create checkout'
);
@@ -2854,7 +2854,7 @@ export async function completeTenantPackagePurchase(params: {
const payload: Record<string, unknown> = { package_id: packageId };
if (orderId) {
payload.lemonsqueezy_order_id = orderId;
payload.paypal_order_id = orderId;
}
const response = await authorizedFetch('/api/v1/tenant/packages/complete', {

View File

@@ -2,7 +2,7 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import toast from 'react-hot-toast';
import { createTenantLemonSqueezyCheckout } from '../../api';
import { createTenantPayPalCheckout } from '../../api';
import { adminPath } from '../../constants';
import { getApiErrorMessage } from '../../lib/apiError';
import { storePendingCheckout } from '../lib/billingCheckout';
@@ -33,7 +33,7 @@ export function usePackageCheckout(): {
cancelUrl.searchParams.set('checkout', 'cancel');
cancelUrl.searchParams.set('package_id', String(packageId));
const { checkout_url, checkout_session_id } = await createTenantLemonSqueezyCheckout(packageId, {
const { approve_url, order_id, checkout_session_id } = await createTenantPayPalCheckout(packageId, {
success_url: successUrl.toString(),
return_url: cancelUrl.toString(),
});
@@ -43,10 +43,15 @@ export function usePackageCheckout(): {
packageId,
checkoutSessionId: checkout_session_id,
startedAt: Date.now(),
orderId: order_id,
});
}
window.location.href = checkout_url;
if (!approve_url) {
throw new Error('PayPal checkout URL missing.');
}
window.location.href = approve_url;
} catch (err) {
toast.error(getApiErrorMessage(err, t('shop.errors.checkout', 'Checkout failed')));
setBusy(false);

View File

@@ -1,6 +1,7 @@
export type PendingCheckout = {
packageId: number | null;
checkoutSessionId?: string | null;
orderId?: string | null;
startedAt: number;
};
@@ -36,12 +37,14 @@ export function loadPendingCheckout(
? parsed.packageId
: null;
const checkoutSessionId = typeof parsed.checkoutSessionId === 'string' ? parsed.checkoutSessionId : null;
const orderId = typeof parsed.orderId === 'string' ? parsed.orderId : null;
if (now - parsed.startedAt > ttl) {
return null;
}
return {
packageId,
checkoutSessionId,
orderId,
startedAt: parsed.startedAt,
};
} catch {