Files
app/ts_web/elements/account/views/paddlesetup.ts
T

112 lines
3.6 KiB
TypeScript
Raw Normal View History

import {
customElement,
DeesElement,
property,
html,
cssManager,
unsafeCSS,
css,
} from '@design.estate/dees-element';
import * as plugins from '../../../plugins.js';
import sharedStyles from '../sharedstyles.js';
import * as state from '../../../states/accountstate.js';
import { IdpState } from '../../../states/idp.state.js';
declare global {
interface HTMLElementTagNameMap {
'lele-accountview-paddlesetup': PaddleSetupView;
}
}
@customElement('lele-accountview-paddlesetup')
export class PaddleSetupView extends DeesElement {
public static styles = [
cssManager.defaultStyles,
sharedStyles,
css`
:host {
display: block;
max-width: 900px;
margin: auto;
color: ${cssManager.bdTheme('#333', '#fff')};
}
`,
];
public render() {
return html`
<h1>-> Paddle Setup</h1>
<p>
In order to use workspace.global <b>with paid features</b>, you need to setup a Paddle
subscription. A Paddle connection is bound to an organization.
</p>
<p>
The base price of a Paddle Subscription is always 0€. Any charges that occur will be billed
as an extra charge on top of your free base subscription
<b>on a monthly date of your choosing</b>.
</p>
<p>
Since Paddle acts as merchant of record, your invoices will read Paddle as Creditor, and you
as Debitor.
</p>
<h2>Why are we using Paddle?</h2>
<p>
Paddle takes care of tax compliance for us. This allows us to sell our products world wide
while Paddle makes sure any sales are in compliance with local laws.
</p>
2025-12-01 04:44:47 +00:00
<dees-button @clicked=${() => this.openPaddle()}>Let's do it!</dees-button>
`;
}
2025-12-01 04:44:47 +00:00
public async openPaddle() {
await this.domtoolsPromise;
const paddleButton = this.shadowRoot.querySelector('dees-button');
const idpState = await IdpState.getSingletonInstance();
// Get user email - first try from state, then fetch directly
let userEmail = state.accountState.getState().user?.data?.email;
if (!userEmail) {
// State not loaded, fetch user directly
const whoIsResponse = await idpState.idpClient.whoIs().catch(() => null);
userEmail = whoIsResponse?.user?.data?.email;
}
if (!userEmail) {
console.error('Unable to get user email for Paddle checkout');
paddleButton.status = 'error';
paddleButton.text = 'Error: Not logged in';
return;
}
// Fetch Paddle config from backend
const configRequest = idpState.idpClient.typedsocket
.createTypedRequest<plugins.idpInterfaces.request.IReq_GetPaddleConfig>('getPaddleConfig');
const { paddleToken, paddlePriceId } = await configRequest.fire({});
await this.domtools.setExternalScript('https://cdn.paddle.com/paddle/v2/paddle.js');
globalThis.Paddle.Initialize({
token: paddleToken,
2025-12-01 04:44:47 +00:00
eventCallback: async (dataArg: any) => {
// Paddle Billing v2 event handling
if (dataArg.name === 'checkout.completed') {
2025-12-01 04:44:47 +00:00
const paddleIframe = document.body.querySelector('iframe');
if (paddleIframe) {
document.body.removeChild(paddleIframe);
}
2025-12-01 04:44:47 +00:00
paddleButton.status = 'pending';
paddleButton.text = 'Processing...';
await state.accountState.dispatchAction(state.updatePaddleCheckoutId, dataArg.data.transaction_id);
2025-12-01 04:44:47 +00:00
paddleButton.status = 'success';
paddleButton.text = 'Paddle connected!'
}
},
});
globalThis.Paddle.Checkout.open({
items: [{ priceId: paddlePriceId, quantity: 1 }],
customer: { email: userEmail },
});
}
}